summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/ruby.h40
-rw-r--r--include/ruby/assert.h316
-rw-r--r--include/ruby/atomic.h1145
-rw-r--r--include/ruby/backward.h19
-rw-r--r--include/ruby/backward/2/assume.h56
-rw-r--r--include/ruby/backward/2/attributes.h161
-rw-r--r--include/ruby/backward/2/bool.h36
-rw-r--r--include/ruby/backward/2/gcc_version_since.h37
-rw-r--r--include/ruby/backward/2/inttypes.h131
-rw-r--r--include/ruby/backward/2/limits.h99
-rw-r--r--include/ruby/backward/2/long_long.h73
-rw-r--r--include/ruby/backward/2/r_cast.h32
-rw-r--r--include/ruby/backward/2/rmodule.h36
-rw-r--r--include/ruby/backward/2/stdalign.h30
-rw-r--r--include/ruby/backward/2/stdarg.h69
-rw-r--r--include/ruby/backward/cxxanyargs.hpp671
-rw-r--r--include/ruby/debug.h834
-rw-r--r--include/ruby/defines.h116
-rw-r--r--include/ruby/encoding.h31
-rw-r--r--include/ruby/fiber/scheduler.h505
-rw-r--r--include/ruby/intern.h64
-rw-r--r--include/ruby/internal/abi.h58
-rw-r--r--include/ruby/internal/anyargs.h398
-rw-r--r--include/ruby/internal/arithmetic.h39
-rw-r--r--include/ruby/internal/arithmetic/char.h81
-rw-r--r--include/ruby/internal/arithmetic/double.h72
-rw-r--r--include/ruby/internal/arithmetic/fixnum.h60
-rw-r--r--include/ruby/internal/arithmetic/gid_t.h41
-rw-r--r--include/ruby/internal/arithmetic/int.h264
-rw-r--r--include/ruby/internal/arithmetic/intptr_t.h86
-rw-r--r--include/ruby/internal/arithmetic/long.h356
-rw-r--r--include/ruby/internal/arithmetic/long_long.h135
-rw-r--r--include/ruby/internal/arithmetic/mode_t.h41
-rw-r--r--include/ruby/internal/arithmetic/off_t.h62
-rw-r--r--include/ruby/internal/arithmetic/pid_t.h41
-rw-r--r--include/ruby/internal/arithmetic/short.h113
-rw-r--r--include/ruby/internal/arithmetic/size_t.h66
-rw-r--r--include/ruby/internal/arithmetic/st_data_t.h75
-rw-r--r--include/ruby/internal/arithmetic/uid_t.h41
-rw-r--r--include/ruby/internal/assume.h87
-rw-r--r--include/ruby/internal/attr/alloc_size.h32
-rw-r--r--include/ruby/internal/attr/artificial.h46
-rw-r--r--include/ruby/internal/attr/cold.h37
-rw-r--r--include/ruby/internal/attr/const.h46
-rw-r--r--include/ruby/internal/attr/constexpr.h84
-rw-r--r--include/ruby/internal/attr/deprecated.h82
-rw-r--r--include/ruby/internal/attr/diagnose_if.h42
-rw-r--r--include/ruby/internal/attr/enum_extensibility.h32
-rw-r--r--include/ruby/internal/attr/error.h32
-rw-r--r--include/ruby/internal/attr/flag_enum.h33
-rw-r--r--include/ruby/internal/attr/forceinline.h40
-rw-r--r--include/ruby/internal/attr/format.h38
-rw-r--r--include/ruby/internal/attr/maybe_unused.h38
-rw-r--r--include/ruby/internal/attr/noalias.h69
-rw-r--r--include/ruby/internal/attr/nodiscard.h45
-rw-r--r--include/ruby/internal/attr/noexcept.h91
-rw-r--r--include/ruby/internal/attr/noinline.h35
-rw-r--r--include/ruby/internal/attr/nonnull.h34
-rw-r--r--include/ruby/internal/attr/nonstring.h40
-rw-r--r--include/ruby/internal/attr/noreturn.h48
-rw-r--r--include/ruby/internal/attr/packed_struct.h43
-rw-r--r--include/ruby/internal/attr/pure.h43
-rw-r--r--include/ruby/internal/attr/restrict.h44
-rw-r--r--include/ruby/internal/attr/returns_nonnull.h37
-rw-r--r--include/ruby/internal/attr/warning.h32
-rw-r--r--include/ruby/internal/attr/weakref.h32
-rw-r--r--include/ruby/internal/cast.h50
-rw-r--r--include/ruby/internal/compiler_is.h45
-rw-r--r--include/ruby/internal/compiler_is/apple.h40
-rw-r--r--include/ruby/internal/compiler_is/clang.h37
-rw-r--r--include/ruby/internal/compiler_is/gcc.h45
-rw-r--r--include/ruby/internal/compiler_is/intel.h40
-rw-r--r--include/ruby/internal/compiler_is/msvc.h45
-rw-r--r--include/ruby/internal/compiler_is/sunpro.h54
-rw-r--r--include/ruby/internal/compiler_since.h61
-rw-r--r--include/ruby/internal/config.h151
-rw-r--r--include/ruby/internal/constant_p.h38
-rw-r--r--include/ruby/internal/core.h35
-rw-r--r--include/ruby/internal/core/rarray.h405
-rw-r--r--include/ruby/internal/core/rbasic.h172
-rw-r--r--include/ruby/internal/core/rbignum.h80
-rw-r--r--include/ruby/internal/core/rclass.h93
-rw-r--r--include/ruby/internal/core/rdata.h363
-rw-r--r--include/ruby/internal/core/rfile.h51
-rw-r--r--include/ruby/internal/core/rhash.h131
-rw-r--r--include/ruby/internal/core/rmatch.h144
-rw-r--r--include/ruby/internal/core/robject.h142
-rw-r--r--include/ruby/internal/core/rregexp.h168
-rw-r--r--include/ruby/internal/core/rstring.h453
-rw-r--r--include/ruby/internal/core/rstruct.h109
-rw-r--r--include/ruby/internal/core/rtypeddata.h761
-rw-r--r--include/ruby/internal/ctype.h545
-rw-r--r--include/ruby/internal/dllexport.h80
-rw-r--r--include/ruby/internal/dosish.h89
-rw-r--r--include/ruby/internal/encoding/coderange.h202
-rw-r--r--include/ruby/internal/encoding/ctype.h258
-rw-r--r--include/ruby/internal/encoding/encoding.h1044
-rw-r--r--include/ruby/internal/encoding/pathname.h184
-rw-r--r--include/ruby/internal/encoding/re.h46
-rw-r--r--include/ruby/internal/encoding/sprintf.h78
-rw-r--r--include/ruby/internal/encoding/string.h346
-rw-r--r--include/ruby/internal/encoding/symbol.h100
-rw-r--r--include/ruby/internal/encoding/transcode.h562
-rw-r--r--include/ruby/internal/error.h599
-rw-r--r--include/ruby/internal/eval.h405
-rw-r--r--include/ruby/internal/event.h159
-rw-r--r--include/ruby/internal/fl_type.h757
-rw-r--r--include/ruby/internal/gc.h826
-rw-r--r--include/ruby/internal/glob.h113
-rw-r--r--include/ruby/internal/globals.h211
-rw-r--r--include/ruby/internal/has/attribute.h163
-rw-r--r--include/ruby/internal/has/builtin.h121
-rw-r--r--include/ruby/internal/has/c_attribute.h50
-rw-r--r--include/ruby/internal/has/cpp_attribute.h86
-rw-r--r--include/ruby/internal/has/declspec_attribute.h47
-rw-r--r--include/ruby/internal/has/extension.h33
-rw-r--r--include/ruby/internal/has/feature.h31
-rw-r--r--include/ruby/internal/has/warning.h31
-rw-r--r--include/ruby/internal/intern/array.h663
-rw-r--r--include/ruby/internal/intern/bignum.h846
-rw-r--r--include/ruby/internal/intern/class.h394
-rw-r--r--include/ruby/internal/intern/compar.h62
-rw-r--r--include/ruby/internal/intern/complex.h249
-rw-r--r--include/ruby/internal/intern/cont.h285
-rw-r--r--include/ruby/internal/intern/dir.h42
-rw-r--r--include/ruby/internal/intern/enum.h73
-rw-r--r--include/ruby/internal/intern/enumerator.h263
-rw-r--r--include/ruby/internal/intern/error.h291
-rw-r--r--include/ruby/internal/intern/eval.h222
-rw-r--r--include/ruby/internal/intern/file.h216
-rw-r--r--include/ruby/internal/intern/hash.h306
-rw-r--r--include/ruby/internal/intern/io.h661
-rw-r--r--include/ruby/internal/intern/load.h255
-rw-r--r--include/ruby/internal/intern/marshal.h112
-rw-r--r--include/ruby/internal/intern/numeric.h208
-rw-r--r--include/ruby/internal/intern/object.h501
-rw-r--r--include/ruby/internal/intern/parse.h194
-rw-r--r--include/ruby/internal/intern/proc.h357
-rw-r--r--include/ruby/internal/intern/process.h282
-rw-r--r--include/ruby/internal/intern/random.h116
-rw-r--r--include/ruby/internal/intern/range.h89
-rw-r--r--include/ruby/internal/intern/rational.h172
-rw-r--r--include/ruby/internal/intern/re.h244
-rw-r--r--include/ruby/internal/intern/ruby.h77
-rw-r--r--include/ruby/internal/intern/select.h88
-rw-r--r--include/ruby/internal/intern/select/largesize.h214
-rw-r--r--include/ruby/internal/intern/select/posix.h144
-rw-r--r--include/ruby/internal/intern/select/win32.h259
-rw-r--r--include/ruby/internal/intern/set.h111
-rw-r--r--include/ruby/internal/intern/signal.h146
-rw-r--r--include/ruby/internal/intern/sprintf.h159
-rw-r--r--include/ruby/internal/intern/string.h1758
-rw-r--r--include/ruby/internal/intern/struct.h225
-rw-r--r--include/ruby/internal/intern/thread.h492
-rw-r--r--include/ruby/internal/intern/time.h161
-rw-r--r--include/ruby/internal/intern/variable.h628
-rw-r--r--include/ruby/internal/intern/vm.h433
-rw-r--r--include/ruby/internal/interpreter.h304
-rw-r--r--include/ruby/internal/iterator.h472
-rw-r--r--include/ruby/internal/memory.h767
-rw-r--r--include/ruby/internal/method.h205
-rw-r--r--include/ruby/internal/module.h177
-rw-r--r--include/ruby/internal/newobj.h112
-rw-r--r--include/ruby/internal/scan_args.h534
-rw-r--r--include/ruby/internal/special_consts.h362
-rw-r--r--include/ruby/internal/static_assert.h78
-rw-r--r--include/ruby/internal/stdalign.h135
-rw-r--r--include/ruby/internal/stdbool.h39
-rw-r--r--include/ruby/internal/stdckdint.h68
-rw-r--r--include/ruby/internal/symbol.h343
-rw-r--r--include/ruby/internal/value.h133
-rw-r--r--include/ruby/internal/value_type.h450
-rw-r--r--include/ruby/internal/variable.h337
-rw-r--r--include/ruby/internal/warning_push.h124
-rw-r--r--include/ruby/internal/xmalloc.h288
-rw-r--r--include/ruby/io.h1132
-rw-r--r--include/ruby/io/buffer.h110
-rw-r--r--include/ruby/memory_view.h325
-rw-r--r--include/ruby/missing.h342
-rw-r--r--include/ruby/onigmo.h953
-rw-r--r--include/ruby/oniguruma.h8
-rw-r--r--include/ruby/ractor.h278
-rw-r--r--include/ruby/random.h361
-rw-r--r--include/ruby/re.h172
-rw-r--r--include/ruby/regex.h43
-rw-r--r--include/ruby/ruby.h446
-rw-r--r--include/ruby/st.h199
-rw-r--r--include/ruby/subst.h26
-rw-r--r--include/ruby/thread.h345
-rw-r--r--include/ruby/thread_native.h210
-rw-r--r--include/ruby/util.h239
-rw-r--r--include/ruby/version.h159
-rw-r--r--include/ruby/vm.h61
-rw-r--r--include/ruby/win32.h841
194 files changed, 41847 insertions, 0 deletions
diff --git a/include/ruby.h b/include/ruby.h
new file mode 100644
index 0000000000..789804b7b4
--- /dev/null
+++ b/include/ruby.h
@@ -0,0 +1,40 @@
+#ifndef RUBY_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_H 1
+/**
+ * @author $Author$
+ * @date Sun 10 12:06:15 Jun JST 2007
+ * @copyright 2007-2008 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#define HAVE_RUBY_ATOMIC_H 1
+#define HAVE_RUBY_DEBUG_H 1
+#define HAVE_RUBY_DEFINES_H 1
+#define HAVE_RUBY_ENCODING_H 1
+#define HAVE_RUBY_FIBER_SCHEDULER_H 1
+#define HAVE_RUBY_INTERN_H 1
+#define HAVE_RUBY_IO_H 1
+#define HAVE_RUBY_MEMORY_VIEW_H 1
+#define HAVE_RUBY_MISSING_H 1
+#define HAVE_RUBY_ONIGMO_H 1
+#define HAVE_RUBY_ONIGURUMA_H 1
+#define HAVE_RUBY_RACTOR_H 1
+#define HAVE_RUBY_RANDOM_H 1
+#define HAVE_RUBY_RE_H 1
+#define HAVE_RUBY_REGEX_H 1
+#define HAVE_RUBY_RUBY_H 1
+#define HAVE_RUBY_ST_H 1
+#define HAVE_RUBY_THREAD_H 1
+#define HAVE_RUBY_THREAD_NATIVE_H 1
+#define HAVE_RUBY_UTIL_H 1
+#define HAVE_RUBY_VERSION_H 1
+#define HAVE_RUBY_VM_H 1
+#ifdef _WIN32
+#define HAVE_RUBY_WIN32_H 1
+#endif
+
+#include "ruby/ruby.h"
+
+#endif /* RUBY_H */
diff --git a/include/ruby/assert.h b/include/ruby/assert.h
new file mode 100644
index 0000000000..acc5e5bbfc
--- /dev/null
+++ b/include/ruby/assert.h
@@ -0,0 +1,316 @@
+#ifndef RUBY_ASSERT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_ASSERT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @date Wed May 18 00:21:44 JST 1994
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ */
+#include "ruby/internal/assume.h"
+#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/format.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/backward/2/assume.h"
+
+/* RUBY_NDEBUG is very simple: after everything described below are done,
+ * define it with either NDEBUG is undefined (=0) or defined (=1). It is truly
+ * subordinate.
+ *
+ * RUBY_DEBUG versus NDEBUG is complicated. Assertions shall be:
+ *
+ * | -UNDEBUG | -DNDEBUG
+ * ---------------+----------+---------
+ * -URUBY_DEBUG | (*1) | disabled
+ * -DRUBY_DEBUG=0 | disabled | disabled
+ * -DRUBY_DEBUG=1 | enabled | (*2)
+ * -DRUBY_DEBUG | enabled | (*2)
+ *
+ * where:
+ *
+ * - (*1): Assertions shall be silently disabled, no warnings, in favour of
+ * commit 21991e6ca59274e41a472b5256bd3245f6596c90.
+ *
+ * - (*2): Compile-time warnings shall be issued.
+ */
+
+/** @cond INTERNAL_MACRO */
+
+/*
+ * Pro tip: `!!RUBY_DEBUG-1` expands to...
+ *
+ * - `!!(-1)` (== `!0` == `1`) when RUBY_DEBUG is defined to be empty,
+ * - `(!!0)-1` (== `0-1` == `-1`) when RUBY_DEBUG is defined as 0, and
+ * - `(!!n)-1` (== `1-1` == `0`) when RUBY_DEBUG is defined as something else.
+ */
+#if ! defined(RUBY_DEBUG)
+# define RBIMPL_RUBY_DEBUG 0
+#elif !!RUBY_DEBUG-1 < 0
+# define RBIMPL_RUBY_DEBUG 0
+#else
+# define RBIMPL_RUBY_DEBUG 1
+#endif
+
+/*
+ * ISO/IEC 9899 (all past versions) says that "If NDEBUG is defined as a macro
+ * name at the point in the source file where <assert.h> is included, ..."
+ * which means we must not take its defined value into account.
+ */
+#if defined(NDEBUG)
+# define RBIMPL_NDEBUG 1
+#else
+# define RBIMPL_NDEBUG 0
+#endif
+
+/** @endcond */
+
+/* Here we go... */
+#undef RUBY_DEBUG
+#undef RUBY_NDEBUG
+#undef NDEBUG
+#if defined(__DOXYGEN__)
+# /** Define this macro when you want assertions. */
+# define RUBY_DEBUG 0
+# /** Define this macro when you don't want assertions. */
+# define NDEBUG
+# /** This macro is basically the same as #NDEBUG */
+# define RUBY_NDEBUG 1
+
+#elif (RBIMPL_NDEBUG == 1) && (RBIMPL_RUBY_DEBUG == 0)
+# /* Assertions disabled as per request, no conflicts. */
+# define RUBY_DEBUG 0
+# define RUBY_NDEBUG 1
+# define NDEBUG
+
+#elif (RBIMPL_NDEBUG == 0) && (RBIMPL_RUBY_DEBUG == 1)
+# /* Assertions enabled as per request, no conflicts. */
+# define RUBY_DEBUG 1
+# define RUBY_NDEBUG 0
+# /* keep NDEBUG undefined */
+
+#elif (RBIMPL_NDEBUG == 0) && (RBIMPL_RUBY_DEBUG == 0)
+# /* The (*1) situation in above diagram. */
+# define RUBY_DEBUG 0
+# define RUBY_NDEBUG 1
+# define NDEBUG
+
+#elif (RBIMPL_NDEBUG == 1) && (RBIMPL_RUBY_DEBUG == 1)
+# /* The (*2) situation in above diagram. */
+# define RUBY_DEBUG 1
+# define RUBY_NDEBUG 0
+# /* keep NDEBUG undefined */
+
+# if defined(_MSC_VER)
+# pragma message("NDEBUG is ignored because RUBY_DEBUG>0.")
+# elif defined(__GNUC__)
+# pragma GCC warning "NDEBUG is ignored because RUBY_DEBUG>0."
+# else
+# error NDEBUG is ignored because RUBY_DEBUG>0.
+# endif
+#endif
+#undef RBIMPL_NDEBUG
+#undef RBIMPL_RUBY_DEBUG
+
+/** @cond INTERNAL_MACRO */
+#define RBIMPL_ASSERT_NOTHING RBIMPL_CAST((void)0)
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_COLD()
+void rb_assert_failure(const char *file, int line, const char *name, const char *expr);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_COLD()
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 6)
+void rb_assert_failure_detail(const char *file, int line, const char *name, const char *expr, const char *fmt, ...);
+RBIMPL_SYMBOL_EXPORT_END()
+
+#ifdef RUBY_FUNCTION_NAME_STRING
+# define RBIMPL_ASSERT_FUNC RUBY_FUNCTION_NAME_STRING
+#else
+# define RBIMPL_ASSERT_FUNC RBIMPL_CAST((const char *)0)
+#endif
+
+/** @endcond */
+
+/**
+ * Prints the given message, and terminates the entire process abnormally.
+ *
+ * @param mesg The message to display.
+ */
+#if defined(HAVE___VA_OPT__)
+# if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments")
+/* __VA_OPT__ is to be used for the zero variadic macro arguments
+ * cases. */
+RBIMPL_WARNING_IGNORED(-Wgnu-zero-variadic-macro-arguments)
+# endif
+# define RBIMPL_VA_OPT_ARGS(...) __VA_OPT__(,) __VA_ARGS__
+
+# define RUBY_ASSERT_FAIL(mesg, ...) \
+ rb_assert_failure##__VA_OPT__(_detail)( \
+ __FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+#elif !defined(__cplusplus)
+# define RBIMPL_VA_OPT_ARGS(...)
+
+# define RUBY_ASSERT_FAIL(mesg, ...) \
+ rb_assert_failure(__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg)
+#else
+# undef RBIMPL_VA_OPT_ARGS
+
+# define RUBY_ASSERT_FAIL(mesg) \
+ rb_assert_failure(__FILE__, __LINE__, RBIMPL_ASSERT_FUNC, mesg)
+#endif
+
+/**
+ * Asserts that the expression is truthy. If not aborts with the message.
+ *
+ * @param expr What supposedly evaluates to true.
+ * @param mesg The message to display on failure.
+ */
+#if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_MESG(expr, ...) \
+ (RB_LIKELY(expr) ? RBIMPL_ASSERT_NOTHING : RUBY_ASSERT_FAIL(__VA_ARGS__))
+#else
+# define RUBY_ASSERT_MESG(expr, mesg) \
+ (RB_LIKELY(expr) ? RBIMPL_ASSERT_NOTHING : RUBY_ASSERT_FAIL(mesg))
+#endif
+
+/**
+ * A variant of #RUBY_ASSERT that does not interface with #RUBY_DEBUG.
+ *
+ * @copydetails #RUBY_ASSERT
+ */
+#if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_ALWAYS(expr, ...) \
+ RUBY_ASSERT_MESG(expr, #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+#else
+# define RUBY_ASSERT_ALWAYS(expr) RUBY_ASSERT_MESG((expr), #expr)
+#endif
+
+/**
+ * Asserts that the given expression is truthy if and only if #RUBY_DEBUG is truthy.
+ *
+ * @param expr What supposedly evaluates to true.
+ */
+#if RUBY_DEBUG
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT(expr, ...) \
+ RUBY_ASSERT_MESG((expr), #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+# else
+# define RUBY_ASSERT(expr) RUBY_ASSERT_MESG((expr), #expr)
+# endif
+#else
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT(/* expr, */...) RBIMPL_ASSERT_NOTHING
+# else
+# define RUBY_ASSERT(expr) RBIMPL_ASSERT_NOTHING
+# endif
+#endif
+
+/**
+ * A variant of #RUBY_ASSERT that interfaces with #NDEBUG instead of
+ * #RUBY_DEBUG. This almost resembles `assert` C standard macro, except minor
+ * implementation details.
+ *
+ * @copydetails #RUBY_ASSERT
+ */
+/* Currently `RUBY_DEBUG == ! defined(NDEBUG)` is always true. There is no
+ * difference any longer between this one and `RUBY_ASSERT`. */
+#if defined(NDEBUG)
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_NDEBUG(/* expr, */...) RBIMPL_ASSERT_NOTHING
+# else
+# define RUBY_ASSERT_NDEBUG(expr) RBIMPL_ASSERT_NOTHING
+# endif
+#else
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_NDEBUG(expr, ...) \
+ RUBY_ASSERT_MESG((expr), #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+# else
+# define RUBY_ASSERT_NDEBUG(expr) RUBY_ASSERT_MESG((expr), #expr)
+# endif
+#endif
+
+/**
+ * @copydoc #RUBY_ASSERT_WHEN
+ * @param mesg The message to display on failure.
+ */
+#if RUBY_DEBUG
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_MESG_WHEN(cond, /* expr, */...) \
+ RUBY_ASSERT_MESG(__VA_ARGS__)
+# else
+# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) RUBY_ASSERT_MESG((expr), (mesg))
+# endif
+#else
+# if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_MESG_WHEN(cond, expr, ...) \
+ ((cond) ? RUBY_ASSERT_MESG((expr), __VA_ARGS__) : RBIMPL_ASSERT_NOTHING)
+# else
+# define RUBY_ASSERT_MESG_WHEN(cond, expr, mesg) \
+ ((cond) ? RUBY_ASSERT_MESG((expr), (mesg)) : RBIMPL_ASSERT_NOTHING)
+# endif
+#endif
+
+/**
+ * A variant of #RUBY_ASSERT that asserts when either #RUBY_DEBUG or `cond`
+ * parameter is truthy.
+ *
+ * @param cond Extra condition that shall hold for assertion to take effect.
+ * @param expr What supposedly evaluates to true.
+ */
+#if defined(RBIMPL_VA_OPT_ARGS)
+# define RUBY_ASSERT_WHEN(cond, expr, ...) \
+ RUBY_ASSERT_MESG_WHEN(cond, expr, #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
+#else
+# define RUBY_ASSERT_WHEN(cond, expr) RUBY_ASSERT_MESG_WHEN((cond), (expr), #expr)
+#endif
+
+/**
+ * A variant of #RUBY_ASSERT that asserts when either #RUBY_DEBUG or built-in
+ * type of `obj` is `type`.
+ *
+ * @param obj Object to check its built-in typue.
+ * @param type Built-in type constant, T_ARRAY, T_STRING, etc.
+ */
+#define RUBY_ASSERT_BUILTIN_TYPE(obj, type) \
+ RUBY_ASSERT(RB_TYPE_P(obj, type), \
+ "Actual type is %s", rb_builtin_type_name(BUILTIN_TYPE(obj)))
+
+/**
+ * This is either #RUBY_ASSERT or #RBIMPL_ASSUME, depending on #RUBY_DEBUG.
+ *
+ * @copydetails #RUBY_ASSERT
+ */
+#if RUBY_DEBUG
+# define RBIMPL_ASSERT_OR_ASSUME RUBY_ASSERT_ALWAYS
+#elif ! defined(RBIMPL_VA_OPT_ARGS)
+# define RBIMPL_ASSERT_OR_ASSUME(expr) RBIMPL_ASSUME(expr)
+#elif RBIMPL_COMPILER_BEFORE(Clang, 7, 0, 0)
+# /* See commit 67d259c5dccd31fe49d417fec169977712ffdf10 */
+# define RBIMPL_ASSERT_OR_ASSUME(...) RBIMPL_ASSERT_NOTHING
+#elif defined(RUBY_ASSERT_NOASSUME)
+# /* See commit d300a734414ef6de7e8eb563b7cc4389c455ed08 */
+# define RBIMPL_ASSERT_OR_ASSUME(...) RBIMPL_ASSERT_NOTHING
+#elif ! defined(RBIMPL_HAVE___ASSUME)
+# define RBIMPL_ASSERT_OR_ASSUME(...) RBIMPL_ASSERT_NOTHING
+#else
+# define RBIMPL_ASSERT_OR_ASSUME(expr, ...) RBIMPL_ASSUME(expr)
+#endif
+
+#endif /* RUBY_ASSERT_H */
diff --git a/include/ruby/atomic.h b/include/ruby/atomic.h
new file mode 100644
index 0000000000..fcc48f532c
--- /dev/null
+++ b/include/ruby/atomic.h
@@ -0,0 +1,1145 @@
+#ifndef RUBY_ATOMIC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_ATOMIC_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Atomic operations
+ *
+ * Basically, if we could assume either C11 or C++11, these macros are just
+ * redundant. Sadly we cannot. We have to do them ourselves.
+ */
+
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h> /* size_t */
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h> /* ssize_t */
+#endif
+
+#if RBIMPL_COMPILER_IS(MSVC)
+# pragma intrinsic(_InterlockedOr)
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+# include <atomic.h>
+#endif
+
+#include "ruby/assert.h"
+#include "ruby/backward/2/limits.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/static_assert.h"
+#include "ruby/internal/stdbool.h"
+
+/*
+ * Asserts that your environment supports more than one atomic types. These
+ * days systems tend to have such property (C11 was a standard of decades ago,
+ * right?) but we still support older ones.
+ */
+#if defined(__DOXYGEN__) || defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
+# define RUBY_ATOMIC_GENERIC_MACRO 1
+#endif
+
+/**
+ * Type that is eligible for atomic operations. Depending on your host
+ * platform you might have more than one such type, but we choose one of them
+ * anyways.
+ */
+#if defined(__DOXYGEN__)
+using rb_atomic_t = std::atomic<unsigned>;
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+typedef unsigned int rb_atomic_t;
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+typedef unsigned int rb_atomic_t;
+#elif defined(_WIN32)
+# include <winsock2.h> // to prevent macro redefinitions
+# include <windows.h> // for `LONG` and `Interlocked` functions
+typedef LONG rb_atomic_t;
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+typedef unsigned int rb_atomic_t;
+#elif defined(HAVE_STDATOMIC_H)
+# include <stdatomic.h>
+typedef unsigned int rb_atomic_t;
+#else
+# error No atomic operation found
+#endif
+
+/* Memory ordering constants */
+#if defined(HAVE_GCC_ATOMIC_BUILTINS)
+# define RBIMPL_ATOMIC_RELAXED __ATOMIC_RELAXED
+# define RBIMPL_ATOMIC_ACQUIRE __ATOMIC_ACQUIRE
+# define RBIMPL_ATOMIC_RELEASE __ATOMIC_RELEASE
+# define RBIMPL_ATOMIC_ACQ_REL __ATOMIC_ACQ_REL
+# define RBIMPL_ATOMIC_SEQ_CST __ATOMIC_SEQ_CST
+#elif defined(HAVE_STDATOMIC_H)
+# define RBIMPL_ATOMIC_RELAXED memory_order_relaxed
+# define RBIMPL_ATOMIC_ACQUIRE memory_order_acquire
+# define RBIMPL_ATOMIC_RELEASE memory_order_release
+# define RBIMPL_ATOMIC_ACQ_REL memory_order_acq_rel
+# define RBIMPL_ATOMIC_SEQ_CST memory_order_seq_cst
+#else
+/* Dummy values for unsupported platforms */
+# define RBIMPL_ATOMIC_RELAXED 0
+# define RBIMPL_ATOMIC_ACQUIRE 1
+# define RBIMPL_ATOMIC_RELEASE 2
+# define RBIMPL_ATOMIC_ACQ_REL 3
+# define RBIMPL_ATOMIC_SEQ_CST 4
+#endif
+
+/**
+ * Atomically replaces the value pointed by `var` with the result of addition
+ * of `val` to the old value of `var`.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @param val Value to add.
+ * @return What was stored in `var` before the addition.
+ * @post `var` holds `var + val`.
+ */
+#define RUBY_ATOMIC_FETCH_ADD(var, val) rbimpl_atomic_fetch_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Atomically replaces the value pointed by `var` with the result of
+ * subtraction of `val` to the old value of `var`.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @param val Value to subtract.
+ * @return What was stored in `var` before the subtraction.
+ * @post `var` holds `var - val`.
+ */
+#define RUBY_ATOMIC_FETCH_SUB(var, val) rbimpl_atomic_fetch_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Atomically replaces the value pointed by `var` with the result of
+ * bitwise OR between `val` and the old value of `var`.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @param val Value to mix.
+ * @return void
+ * @post `var` holds `var | val`.
+ * @note For portability, this macro can return void.
+ */
+#define RUBY_ATOMIC_OR(var, val) rbimpl_atomic_or(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Atomically replaces the value pointed by `var` with `val`. This is just an
+ * assignment, but you can additionally know the previous value.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @param val Value to set.
+ * @return What was stored in `var` before the assignment.
+ * @post `var` holds `val`.
+ */
+#define RUBY_ATOMIC_EXCHANGE(var, val) rbimpl_atomic_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Atomic compare-and-swap. This stores `val` to `var` if and only if the
+ * assignment changes the value of `var` from `oldval` to `newval`. You can
+ * detect whether the assignment happened or not using the return value.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @param oldval Expected value of `var` before the assignment.
+ * @param newval What you want to store at `var`.
+ * @retval oldval Successful assignment (`var` is now `newval`).
+ * @retval otherwise Something else is at `var`; not updated.
+ */
+#define RUBY_ATOMIC_CAS(var, oldval, newval) \
+ rbimpl_atomic_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Atomic load. This loads `var` with an atomic intrinsic and returns
+ * its value.
+ *
+ * @param var A variable of ::rb_atomic_t
+ * @return What was stored in `var`j
+ */
+#define RUBY_ATOMIC_LOAD(var) rbimpl_atomic_load(&(var), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_EXCHANGE, except for the return type.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @param val Value to set.
+ * @return void
+ * @post `var` holds `val`.
+ */
+#define RUBY_ATOMIC_SET(var, val) rbimpl_atomic_store(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_FETCH_ADD, except for the return type.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @param val Value to add.
+ * @return void
+ * @post `var` holds `var + val`.
+ */
+#define RUBY_ATOMIC_ADD(var, val) rbimpl_atomic_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_FETCH_SUB, except for the return type.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @param val Value to subtract.
+ * @return void
+ * @post `var` holds `var - val`.
+ */
+#define RUBY_ATOMIC_SUB(var, val) rbimpl_atomic_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Atomically increments the value pointed by `var`.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @return void
+ * @post `var` holds `var + 1`.
+ */
+#define RUBY_ATOMIC_INC(var) rbimpl_atomic_inc(&(var), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Atomically decrements the value pointed by `var`.
+ *
+ * @param var A variable of ::rb_atomic_t.
+ * @return void
+ * @post `var` holds `var - 1`.
+ */
+#define RUBY_ATOMIC_DEC(var) rbimpl_atomic_dec(&(var), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_FETCH_ADD, except it expects its arguments to be `size_t`.
+ * There are cases where ::rb_atomic_t is 32bit while `size_t` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `size_t`.
+ * @param val Value to add.
+ * @return What was stored in `var` before the addition.
+ * @post `var` holds `var + val`.
+ */
+#define RUBY_ATOMIC_SIZE_FETCH_ADD(var, val) rbimpl_atomic_size_fetch_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_INC, except it expects its argument is `size_t`.
+ * There are cases where ::rb_atomic_t is 32bit while `size_t` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `size_t`.
+ * @return void
+ * @post `var` holds `var + 1`.
+ */
+#define RUBY_ATOMIC_SIZE_INC(var) rbimpl_atomic_size_inc(&(var), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_DEC, except it expects its argument is `size_t`.
+ * There are cases where ::rb_atomic_t is 32bit while `size_t` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `size_t`.
+ * @return void
+ * @post `var` holds `var - 1`.
+ */
+#define RUBY_ATOMIC_SIZE_DEC(var) rbimpl_atomic_size_dec(&(var), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are
+ * `size_t`. There are cases where ::rb_atomic_t is 32bit while `size_t` is
+ * 64bit. This should be used for size related operations to support such
+ * platforms.
+ *
+ * @param var A variable of `size_t`.
+ * @param val Value to set.
+ * @return What was stored in `var` before the assignment.
+ * @post `var` holds `val`.
+ */
+#define RUBY_ATOMIC_SIZE_EXCHANGE(var, val) \
+ rbimpl_atomic_size_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `size_t`.
+ * There are cases where ::rb_atomic_t is 32bit while `size_t` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `size_t`.
+ * @param oldval Expected value of `var` before the assignment.
+ * @param newval What you want to store at `var`.
+ * @retval oldval Successful assignment (`var` is now `newval`).
+ * @retval otherwise Something else is at `var`; not updated.
+ */
+#define RUBY_ATOMIC_SIZE_CAS(var, oldval, newval) \
+ rbimpl_atomic_size_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_ADD, except it expects its arguments are `size_t`.
+ * There are cases where ::rb_atomic_t is 32bit while `size_t` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `size_t`.
+ * @param val Value to add.
+ * @return void
+ * @post `var` holds `var + val`.
+ */
+#define RUBY_ATOMIC_SIZE_ADD(var, val) rbimpl_atomic_size_add(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_SUB, except it expects its arguments are `size_t`.
+ * There are cases where ::rb_atomic_t is 32bit while `size_t` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `size_t`.
+ * @param val Value to subtract.
+ * @return void
+ * @post `var` holds `var - val`.
+ */
+#define RUBY_ATOMIC_SIZE_SUB(var, val) rbimpl_atomic_size_sub(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are
+ * `void*`. There are cases where ::rb_atomic_t is 32bit while `void*` is
+ * 64bit. This should be used for pointer related operations to support such
+ * platforms.
+ *
+ * @param var A variable of `void *`.
+ * @param val Value to set.
+ * @return What was stored in `var` before the assignment.
+ * @post `var` holds `val`.
+ *
+ * @internal
+ *
+ * :FIXME: this `(void*)` cast is evil! However `void*` is incompatible with
+ * some pointers, most notably function pointers.
+ */
+#define RUBY_ATOMIC_PTR_EXCHANGE(var, val) \
+ RBIMPL_CAST(rbimpl_atomic_ptr_exchange((void **)&(var), (void *)val, RBIMPL_ATOMIC_SEQ_CST))
+
+/**
+ * Identical to #RUBY_ATOMIC_LOAD, except it expects its arguments are `void*`.
+ * There are cases where ::rb_atomic_t is 32bit while `void*` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `void*`
+ * @return The value of `var` (without tearing)
+ */
+#define RUBY_ATOMIC_PTR_LOAD(var) \
+ RBIMPL_CAST(rbimpl_atomic_ptr_load((void **)&var, RBIMPL_ATOMIC_SEQ_CST))
+
+/**
+* Identical to #RUBY_ATOMIC_SET, except it expects its arguments are
+* `void*`. There are cases where ::rb_atomic_t is 32bit while ::VALUE is
+* 64bit. This should be used for pointer related operations to support such
+* platforms.
+*
+* @param var A variable of `void*`.
+* @param val Value to set.
+* @post `var` holds `val`.
+*/
+#define RUBY_ATOMIC_PTR_SET(var, val) \
+ rbimpl_atomic_ptr_store((volatile void **)&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are `void*`.
+ * There are cases where ::rb_atomic_t is 32bit while `void*` is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `void*`.
+ * @param oldval Expected value of `var` before the assignment.
+ * @param newval What you want to store at `var`.
+ * @retval oldval Successful assignment (`var` is now `newval`).
+ * @retval otherwise Something else is at `var`; not updated.
+ */
+#define RUBY_ATOMIC_PTR_CAS(var, oldval, newval) \
+ RBIMPL_CAST(rbimpl_atomic_ptr_cas((void **)&(var), (void *)(oldval), (void *)(newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST))
+
+/**
+ * Identical to #RUBY_ATOMIC_SET, except it expects its arguments are
+ * ::VALUE. There are cases where ::rb_atomic_t is 32bit while ::VALUE is
+ * 64bit. This should be used for pointer related operations to support such
+ * platforms.
+ *
+ * @param var A variable of ::VALUE.
+ * @param val Value to set.
+ * @post `var` holds `val`.
+ */
+#define RUBY_ATOMIC_VALUE_SET(var, val) \
+ rbimpl_atomic_value_store(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_EXCHANGE, except it expects its arguments are
+ * ::VALUE. There are cases where ::rb_atomic_t is 32bit while ::VALUE is
+ * 64bit. This should be used for pointer related operations to support such
+ * platforms.
+ *
+ * @param var A variable of ::VALUE.
+ * @param val Value to set.
+ * @return What was stored in `var` before the assignment.
+ * @post `var` holds `val`.
+ */
+#define RUBY_ATOMIC_VALUE_EXCHANGE(var, val) \
+ rbimpl_atomic_value_exchange(&(var), (val), RBIMPL_ATOMIC_SEQ_CST)
+
+/**
+ * Identical to #RUBY_ATOMIC_CAS, except it expects its arguments are ::VALUE.
+ * There are cases where ::rb_atomic_t is 32bit while ::VALUE is 64bit. This
+ * should be used for size related operations to support such platforms.
+ *
+ * @param var A variable of `void*`.
+ * @param oldval Expected value of `var` before the assignment.
+ * @param newval What you want to store at `var`.
+ * @retval oldval Successful assignment (`var` is now `newval`).
+ * @retval otherwise Something else is at `var`; not updated.
+ */
+#define RUBY_ATOMIC_VALUE_CAS(var, oldval, newval) \
+ rbimpl_atomic_value_cas(&(var), (oldval), (newval), RBIMPL_ATOMIC_SEQ_CST, RBIMPL_ATOMIC_SEQ_CST)
+
+/** @cond INTERNAL_MACRO */
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline rb_atomic_t
+rbimpl_atomic_fetch_add(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_fetch_add(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ return __sync_fetch_and_add(ptr, val);
+
+#elif defined(_WIN32)
+ return InterlockedExchangeAdd(ptr, val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ /*
+ * `atomic_add_int_nv` takes its second argument as `int`! Meanwhile our
+ * `rb_atomic_t` is unsigned. We cannot pass `val` as-is. We have to
+ * manually check integer overflow.
+ */
+ RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX);
+ return atomic_add_int_nv(ptr, val) - val;
+
+#elif defined(HAVE_STDATOMIC_H)
+ return atomic_fetch_add_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+/** @cond INTERNAL_MACRO */
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline size_t
+rbimpl_atomic_size_fetch_add(volatile size_t *ptr, size_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_fetch_add(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ return __sync_fetch_and_add(ptr, val);
+
+#elif defined(_WIN32)
+ return InterlockedExchangeAdd64(ptr, val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
+ /* Ditto for `atomic_add_int_nv`. */
+ RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX);
+ atomic_add_long(ptr, val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
+
+ volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
+ rbimpl_atomic_fetch_add(tmp, val, memory_order);
+
+#elif defined(HAVE_STDATOMIC_H)
+ return atomic_fetch_add_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_add(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ /*
+ * GCC on amd64 is smart enough to detect this `__atomic_add_fetch`'s
+ * return value is not used, then compiles it into single `LOCK ADD`
+ * instruction.
+ */
+ __atomic_add_fetch(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ __sync_add_and_fetch(ptr, val);
+
+#elif defined(_WIN32)
+ /*
+ * `InterlockedExchangeAdd` is `LOCK XADD`. It seems there also is
+ * `_InterlockedAdd` intrinsic in ARM Windows but not for x86? Sticking to
+ * `InterlockedExchangeAdd` for better portability.
+ */
+ InterlockedExchangeAdd(ptr, val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ /* Ditto for `atomic_add_int_nv`. */
+ RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX);
+ atomic_add_int(ptr, val);
+
+#elif defined(HAVE_STDATOMIC_H)
+ atomic_fetch_add_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_size_add(volatile size_t *ptr, size_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ __atomic_add_fetch(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ __sync_add_and_fetch(ptr, val);
+
+#elif defined(_WIN64)
+ /* Ditto for `InterlockeExchangedAdd`. */
+ InterlockedExchangeAdd64(ptr, val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
+ /* Ditto for `atomic_add_int_nv`. */
+ RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX);
+ atomic_add_long(ptr, val);
+
+#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
+ RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
+
+ volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
+ rbimpl_atomic_add(tmp, val, memory_order);
+
+#elif defined(HAVE_STDATOMIC_H)
+ atomic_fetch_add_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_inc(volatile rb_atomic_t *ptr, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
+ rbimpl_atomic_add(ptr, 1, memory_order);
+
+#elif defined(_WIN32)
+ InterlockedIncrement(ptr);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ atomic_inc_uint(ptr);
+
+#elif defined(HAVE_STDATOMIC_H)
+ rbimpl_atomic_add(ptr, 1, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_size_inc(volatile size_t *ptr, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
+ rbimpl_atomic_size_add(ptr, 1, memory_order);
+
+#elif defined(_WIN64)
+ InterlockedIncrement64(ptr);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
+ atomic_inc_ulong(ptr);
+
+#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
+ RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
+
+ rbimpl_atomic_size_add(ptr, 1, memory_order);
+
+#elif defined(HAVE_STDATOMIC_H)
+ rbimpl_atomic_size_add(ptr, 1, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline rb_atomic_t
+rbimpl_atomic_fetch_sub(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_fetch_sub(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ return __sync_fetch_and_sub(ptr, val);
+
+#elif defined(_WIN32)
+ /* rb_atomic_t is signed here! Safe to do `-val`. */
+ return InterlockedExchangeAdd(ptr, -val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ /* Ditto for `rbimpl_atomic_fetch_add`. */
+ const signed neg = -1;
+ RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX);
+ return atomic_add_int_nv(ptr, neg * val) + val;
+
+#elif defined(HAVE_STDATOMIC_H)
+ return atomic_fetch_sub_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_sub(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ __atomic_sub_fetch(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ __sync_sub_and_fetch(ptr, val);
+
+#elif defined(_WIN32)
+ InterlockedExchangeAdd(ptr, -val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ const signed neg = -1;
+ RBIMPL_ASSERT_OR_ASSUME(val <= INT_MAX);
+ atomic_add_int(ptr, neg * val);
+
+#elif defined(HAVE_STDATOMIC_H)
+ atomic_fetch_sub_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_size_sub(volatile size_t *ptr, size_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ __atomic_sub_fetch(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ __sync_sub_and_fetch(ptr, val);
+
+#elif defined(_WIN64)
+ const ssize_t neg = -1;
+ InterlockedExchangeAdd64(ptr, neg * val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
+ const signed neg = -1;
+ RBIMPL_ASSERT_OR_ASSUME(val <= LONG_MAX);
+ atomic_add_long(ptr, neg * val);
+
+#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
+ RBIMPL_STATIC_ASSERT(size_of_rb_atomic_t, sizeof *ptr == sizeof(rb_atomic_t));
+
+ volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
+ rbimpl_atomic_sub(tmp, val, memory_order);
+
+#elif defined(HAVE_STDATOMIC_H)
+ atomic_fetch_sub_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_dec(volatile rb_atomic_t *ptr, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
+ rbimpl_atomic_sub(ptr, 1, memory_order);
+
+#elif defined(_WIN32)
+ InterlockedDecrement(ptr);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ atomic_dec_uint(ptr);
+
+#elif defined(HAVE_STDATOMIC_H)
+ rbimpl_atomic_sub(ptr, 1, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_size_dec(volatile size_t *ptr, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS) || defined(HAVE_GCC_SYNC_BUILTINS)
+ rbimpl_atomic_size_sub(ptr, 1, memory_order);
+
+#elif defined(_WIN64)
+ InterlockedDecrement64(ptr);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
+ atomic_dec_ulong(ptr);
+
+#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
+ RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
+
+ rbimpl_atomic_size_sub(ptr, 1, memory_order);
+
+#elif defined(HAVE_STDATOMIC_H)
+ rbimpl_atomic_size_sub(ptr, 1, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_or(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ __atomic_or_fetch(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ __sync_or_and_fetch(ptr, val);
+
+#elif RBIMPL_COMPILER_IS(MSVC)
+ _InterlockedOr(ptr, val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ atomic_or_uint(ptr, val);
+
+#elif !defined(_WIN32) && defined(HAVE_STDATOMIC_H)
+ atomic_fetch_or_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline rb_atomic_t
+rbimpl_atomic_exchange(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_exchange_n(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ return __sync_lock_test_and_set(ptr, val);
+
+#elif defined(_WIN32)
+ return InterlockedExchange(ptr, val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ return atomic_swap_uint(ptr, val);
+
+#elif defined(HAVE_STDATOMIC_H)
+ return atomic_exchange_explicit((_Atomic volatile rb_atomic_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline size_t
+rbimpl_atomic_size_exchange(volatile size_t *ptr, size_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_exchange_n(ptr, val, memory_order);
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ return __sync_lock_test_and_set(ptr, val);
+
+#elif defined(_WIN64)
+ return InterlockedExchange64(ptr, val);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
+ return atomic_swap_ulong(ptr, val);
+
+#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
+ RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
+
+ volatile rb_atomic_t *const tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
+ const rb_atomic_t ret = rbimpl_atomic_exchange(tmp, val, memory_order);
+ return RBIMPL_CAST((size_t)ret);
+
+#elif defined(HAVE_STDATOMIC_H)
+ return atomic_exchange_explicit((_Atomic volatile size_t *)ptr, val, memory_order);
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_size_store(volatile size_t *ptr, size_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ __atomic_store_n(ptr, val, memory_order);
+
+#else
+ rbimpl_atomic_size_exchange(ptr, val, memory_order);
+
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void *
+rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(InterlockedExchangePointer)
+ /* const_cast */
+ PVOID *pptr = RBIMPL_CAST((PVOID *)ptr);
+ PVOID pval = RBIMPL_CAST((PVOID)val);
+ return InterlockedExchangePointer(pptr, pval);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ return atomic_swap_ptr(ptr, RBIMPL_CAST((void *)val));
+
+#else
+ RBIMPL_STATIC_ASSERT(sizeof_voidp, sizeof *ptr == sizeof(size_t));
+
+ const size_t sval = RBIMPL_CAST((size_t)val);
+ volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
+ const size_t sret = rbimpl_atomic_size_exchange(sptr, sval, memory_order);
+ return RBIMPL_CAST((void *)sret);
+
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_ptr_store(volatile void **ptr, void *val, int memory_order)
+{
+ RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
+
+ const size_t sval = RBIMPL_CAST((size_t)val);
+ volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
+ rbimpl_atomic_size_store(sptr, sval, memory_order);
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline VALUE
+rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val, int memory_order)
+{
+ RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
+
+ const size_t sval = RBIMPL_CAST((size_t)val);
+ volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
+ const size_t sret = rbimpl_atomic_size_exchange(sptr, sval, memory_order);
+ return RBIMPL_CAST((VALUE)sret);
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_value_store(volatile VALUE *ptr, VALUE val, int memory_order)
+{
+ RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
+
+ const size_t sval = RBIMPL_CAST((size_t)val);
+ volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
+ rbimpl_atomic_size_store(sptr, sval, memory_order);
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline rb_atomic_t
+rbimpl_atomic_load(volatile rb_atomic_t *ptr, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_load_n(ptr, memory_order);
+#else
+ return rbimpl_atomic_fetch_add(ptr, 0, memory_order);
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void
+rbimpl_atomic_store(volatile rb_atomic_t *ptr, rb_atomic_t val, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ __atomic_store_n(ptr, val, memory_order);
+
+#else
+ /* Maybe std::atomic<rb_atomic_t>::store can be faster? */
+ rbimpl_atomic_exchange(ptr, val, memory_order);
+
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline rb_atomic_t
+rbimpl_atomic_cas(volatile rb_atomic_t *ptr, rb_atomic_t oldval, rb_atomic_t newval, int success_memorder, int failure_memorder)
+{
+ (void)success_memorder;
+ (void)failure_memorder;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ __atomic_compare_exchange_n(
+ ptr, &oldval, newval, 0, success_memorder, failure_memorder);
+ return oldval;
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ return __sync_val_compare_and_swap(ptr, oldval, newval);
+
+#elif RBIMPL_COMPILER_IS(MSVC)
+ return InterlockedCompareExchange(ptr, newval, oldval);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ return atomic_cas_uint(ptr, oldval, newval);
+
+#elif defined(HAVE_STDATOMIC_H)
+ atomic_compare_exchange_strong_explicit(
+ (_Atomic volatile rb_atomic_t *)ptr, &oldval, newval, success_memorder, failure_memorder);
+ return oldval;
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline size_t
+rbimpl_atomic_size_cas(volatile size_t *ptr, size_t oldval, size_t newval, int success_memorder, int failure_memorder)
+{
+ (void)success_memorder;
+ (void)failure_memorder;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ __atomic_compare_exchange_n(
+ ptr, &oldval, newval, 0, success_memorder, failure_memorder);
+ return oldval;
+
+#elif defined(HAVE_GCC_SYNC_BUILTINS)
+ return __sync_val_compare_and_swap(ptr, oldval, newval);
+
+#elif defined(_WIN64)
+ return InterlockedCompareExchange64(ptr, newval, oldval);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H) && (defined(_LP64) || defined(_I32LPx))
+ return atomic_cas_ulong(ptr, oldval, newval);
+
+#elif defined(_WIN32) || (defined(__sun) && defined(HAVE_ATOMIC_H))
+ RBIMPL_STATIC_ASSERT(size_of_size_t, sizeof *ptr == sizeof(rb_atomic_t));
+
+ volatile rb_atomic_t *tmp = RBIMPL_CAST((volatile rb_atomic_t *)ptr);
+ return rbimpl_atomic_cas(tmp, oldval, newval, success_memorder, failure_memorder);
+
+#elif defined(HAVE_STDATOMIC_H)
+ atomic_compare_exchange_strong_explicit(
+ (_Atomic volatile size_t *)ptr, &oldval, newval, success_memorder, failure_memorder);
+ return oldval;
+
+#else
+# error Unsupported platform.
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void *
+rbimpl_atomic_ptr_cas(void **ptr, const void *oldval, const void *newval, int success_memorder, int failure_memorder)
+{
+ (void)success_memorder;
+ (void)failure_memorder;
+#if 0
+
+#elif defined(InterlockedExchangePointer)
+ /* ... Can we say that InterlockedCompareExchangePtr surly exists when
+ * InterlockedExchangePointer is defined? Seems so but...?*/
+ PVOID *pptr = RBIMPL_CAST((PVOID *)ptr);
+ PVOID pold = RBIMPL_CAST((PVOID)oldval);
+ PVOID pnew = RBIMPL_CAST((PVOID)newval);
+ return InterlockedCompareExchangePointer(pptr, pnew, pold);
+
+#elif defined(__sun) && defined(HAVE_ATOMIC_H)
+ void *pold = RBIMPL_CAST((void *)oldval);
+ void *pnew = RBIMPL_CAST((void *)newval);
+ return atomic_cas_ptr(ptr, pold, pnew);
+
+
+#else
+ RBIMPL_STATIC_ASSERT(sizeof_voidp, sizeof *ptr == sizeof(size_t));
+
+ const size_t snew = RBIMPL_CAST((size_t)newval);
+ const size_t sold = RBIMPL_CAST((size_t)oldval);
+ volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
+ const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew, success_memorder, failure_memorder);
+ return RBIMPL_CAST((void *)sret);
+
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline void *
+rbimpl_atomic_ptr_load(void **ptr, int memory_order)
+{
+ (void)memory_order;
+#if 0
+
+#elif defined(HAVE_GCC_ATOMIC_BUILTINS)
+ return __atomic_load_n(ptr, memory_order);
+#else
+ void *val = *ptr;
+ return rbimpl_atomic_ptr_cas(ptr, val, val, memory_order, memory_order);
+#endif
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline VALUE
+rbimpl_atomic_value_load(volatile VALUE *ptr, int memory_order)
+{
+ return RBIMPL_CAST((VALUE)rbimpl_atomic_ptr_load((void **)ptr, memory_order));
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+static inline VALUE
+rbimpl_atomic_value_cas(volatile VALUE *ptr, VALUE oldval, VALUE newval, int success_memorder, int failure_memorder)
+{
+ RBIMPL_STATIC_ASSERT(sizeof_value, sizeof *ptr == sizeof(size_t));
+
+ const size_t snew = RBIMPL_CAST((size_t)newval);
+ const size_t sold = RBIMPL_CAST((size_t)oldval);
+ volatile size_t *const sptr = RBIMPL_CAST((volatile size_t *)ptr);
+ const size_t sret = rbimpl_atomic_size_cas(sptr, sold, snew, success_memorder, failure_memorder);
+ return RBIMPL_CAST((VALUE)sret);
+}
+/** @endcond */
+#endif /* RUBY_ATOMIC_H */
diff --git a/include/ruby/backward.h b/include/ruby/backward.h
new file mode 100644
index 0000000000..6726102158
--- /dev/null
+++ b/include/ruby/backward.h
@@ -0,0 +1,19 @@
+#ifndef RUBY_RUBY_BACKWARD_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_RUBY_BACKWARD_H 1
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#include "ruby/internal/value.h"
+#include "ruby/internal/interpreter.h"
+#include "ruby/backward/2/attributes.h"
+
+/* from version.c */
+#if defined(RUBY_SHOW_COPYRIGHT_TO_DIE) && !!(RUBY_SHOW_COPYRIGHT_TO_DIE+0)
+# error RUBY_SHOW_COPYRIGHT_TO_DIE is deprecated
+#endif
+
+#endif /* RUBY_RUBY_BACKWARD_H */
diff --git a/include/ruby/backward/2/assume.h b/include/ruby/backward/2/assume.h
new file mode 100644
index 0000000000..d148710127
--- /dev/null
+++ b/include/ruby/backward/2/assume.h
@@ -0,0 +1,56 @@
+#ifndef RUBY_BACKWARD2_ASSUME_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_ASSUME_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #ASSUME / #RB_LIKELY / #UNREACHABLE
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/assume.h"
+#include "ruby/internal/has/builtin.h"
+
+#define ASSUME RBIMPL_ASSUME /**< @old{RBIMPL_ASSUME} */
+#define UNREACHABLE RBIMPL_UNREACHABLE() /**< @old{RBIMPL_UNREACHABLE} */
+#define UNREACHABLE_RETURN RBIMPL_UNREACHABLE_RETURN /**< @old{RBIMPL_UNREACHABLE_RETURN} */
+
+/* likely */
+#if RBIMPL_HAS_BUILTIN(__builtin_expect)
+/**
+ * Asserts that the given Boolean expression likely holds.
+ *
+ * @param x An expression that likely holds.
+ *
+ * @note Consider this macro carefully. It has been here since when CPUs were
+ * like babies, but contemporary processors are beasts. They are
+ * smarter than mare mortals like us today. Their branch predictions
+ * highly expectedly outperform your use of this macro.
+ */
+# define RB_LIKELY(x) (__builtin_expect(!!(x), 1))
+
+/**
+ * Asserts that the given Boolean expression likely doesn't hold.
+ *
+ * @param x An expression that likely doesn't hold.
+ */
+# define RB_UNLIKELY(x) (__builtin_expect(!!(x), 0))
+#else
+# define RB_LIKELY(x) (x)
+# define RB_UNLIKELY(x) (x)
+#endif
+
+#endif /* RUBY_BACKWARD2_ASSUME_H */
diff --git a/include/ruby/backward/2/attributes.h b/include/ruby/backward/2/attributes.h
new file mode 100644
index 0000000000..916d9e9d5b
--- /dev/null
+++ b/include/ruby/backward/2/attributes.h
@@ -0,0 +1,161 @@
+#ifndef RUBY_BACKWARD2_ATTRIBUTES_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_ATTRIBUTES_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Various attribute-related macros.
+ *
+ * ### Q&A ###
+ *
+ * - Q: Why are the macros defined in this header file so inconsistent in
+ * style?
+ *
+ * - A: Don't know. Don't blame me. Backward compatibility is the key here.
+ * I'm just preserving what they have been.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/attr/alloc_size.h"
+#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/error.h"
+#include "ruby/internal/attr/forceinline.h"
+#include "ruby/internal/attr/format.h"
+#include "ruby/internal/attr/maybe_unused.h"
+#include "ruby/internal/attr/noinline.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/attr/packed_struct.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/attr/restrict.h"
+#include "ruby/internal/attr/returns_nonnull.h"
+#include "ruby/internal/attr/warning.h"
+#include "ruby/internal/has/attribute.h"
+
+/* function attributes */
+#undef CONSTFUNC
+#define CONSTFUNC(x) RBIMPL_ATTR_CONST() x
+
+#undef PUREFUNC
+#define PUREFUNC(x) RBIMPL_ATTR_PURE() x
+
+#undef DEPRECATED
+#define DEPRECATED(x) RBIMPL_ATTR_DEPRECATED(("")) x
+
+#undef DEPRECATED_BY
+#define DEPRECATED_BY(n,x) RBIMPL_ATTR_DEPRECATED(("by: " # n)) x
+
+#undef DEPRECATED_TYPE
+#if defined(__GNUC__)
+# define DEPRECATED_TYPE(mesg, decl) \
+ _Pragma("message \"DEPRECATED_TYPE is deprecated\""); \
+ decl RBIMPL_ATTR_DEPRECATED(mseg)
+#elif defined(_MSC_VER)
+# pragma deprecated(DEPRECATED_TYPE)
+# define DEPRECATED_TYPE(mesg, decl) \
+ __pragma(message(__FILE__"("STRINGIZE(__LINE__)"): warning: " \
+ "DEPRECATED_TYPE is deprecated")) \
+ decl RBIMPL_ATTR_DEPRECATED(mseg)
+#else
+# define DEPRECATED_TYPE(mesg, decl) \
+ <-<-"DEPRECATED_TYPE is deprecated"->->
+#endif
+
+#undef RUBY_CXX_DEPRECATED
+#define RUBY_CXX_DEPRECATED(mseg) RBIMPL_ATTR_DEPRECATED((mseg))
+
+#undef NOINLINE
+#define NOINLINE(x) RBIMPL_ATTR_NOINLINE() x
+
+#undef ALWAYS_INLINE
+#define ALWAYS_INLINE(x) RBIMPL_ATTR_FORCEINLINE() x
+
+#undef ERRORFUNC
+#define ERRORFUNC(mesg, x) RBIMPL_ATTR_ERROR(mesg) x
+#if RBIMPL_HAS_ATTRIBUTE(error)
+# define HAVE_ATTRIBUTE_ERRORFUNC 1
+#endif
+
+#undef WARNINGFUNC
+#define WARNINGFUNC(mesg, x) RBIMPL_ATTR_WARNING(mesg) x
+#if RBIMPL_HAS_ATTRIBUTE(warning)
+# define HAVE_ATTRIBUTE_WARNINGFUNC 1
+#endif
+
+/*
+ cold attribute for code layout improvements
+ RUBY_FUNC_ATTRIBUTE not used because MSVC does not like nested func macros
+ */
+#undef COLDFUNC
+#define COLDFUNC RBIMPL_ATTR_COLD()
+
+#define PRINTF_ARGS(decl, string_index, first_to_check) \
+ RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, (string_index), (first_to_check)) \
+ decl
+
+#undef RUBY_ATTR_ALLOC_SIZE
+#define RUBY_ATTR_ALLOC_SIZE RBIMPL_ATTR_ALLOC_SIZE
+
+#undef RUBY_ATTR_MALLOC
+#define RUBY_ATTR_MALLOC RBIMPL_ATTR_RESTRICT()
+
+#undef RUBY_ATTR_RETURNS_NONNULL
+#define RUBY_ATTR_RETURNS_NONNULL RBIMPL_ATTR_RETURNS_NONNULL()
+
+#ifndef FUNC_MINIMIZED
+#define FUNC_MINIMIZED(x) x
+#endif
+
+#ifndef FUNC_UNOPTIMIZED
+#define FUNC_UNOPTIMIZED(x) x
+#endif
+
+#ifndef RUBY_ALIAS_FUNCTION_TYPE
+#define RUBY_ALIAS_FUNCTION_TYPE(type, prot, name, args) \
+ FUNC_MINIMIZED(type prot) {return (type)name args;}
+#endif
+
+#ifndef RUBY_ALIAS_FUNCTION_VOID
+#define RUBY_ALIAS_FUNCTION_VOID(prot, name, args) \
+ FUNC_MINIMIZED(void prot) {name args;}
+#endif
+
+#ifndef RUBY_ALIAS_FUNCTION
+#define RUBY_ALIAS_FUNCTION(prot, name, args) \
+ RUBY_ALIAS_FUNCTION_TYPE(VALUE, prot, name, args)
+#endif
+
+#undef RUBY_FUNC_NONNULL
+#define RUBY_FUNC_NONNULL(n, x) RBIMPL_ATTR_NONNULL(n) x
+
+#undef NORETURN
+#define NORETURN(x) RBIMPL_ATTR_NORETURN() x
+#define NORETURN_STYLE_NEW
+
+#undef PACKED_STRUCT
+#define PACKED_STRUCT(x) \
+ RBIMPL_ATTR_PACKED_STRUCT_BEGIN() x RBIMPL_ATTR_PACKED_STRUCT_END()
+
+#undef PACKED_STRUCT_UNALIGNED
+#define PACKED_STRUCT_UNALIGNED(x) \
+ RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() x \
+ RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END()
+
+#undef RB_UNUSED_VAR
+#define RB_UNUSED_VAR(x) x RBIMPL_ATTR_MAYBE_UNUSED()
+
+#endif /* RUBY_BACKWARD2_ATTRIBUTES_H */
diff --git a/include/ruby/backward/2/bool.h b/include/ruby/backward/2/bool.h
new file mode 100644
index 0000000000..f2fa390c80
--- /dev/null
+++ b/include/ruby/backward/2/bool.h
@@ -0,0 +1,36 @@
+#ifndef RUBY_BACKWARD2_BOOL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_BOOL_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines old TRUE / FALSE
+ */
+#include "ruby/internal/stdbool.h"
+
+#ifndef FALSE
+# define FALSE false
+#elif FALSE
+# error FALSE must be false
+#endif
+
+#ifndef TRUE
+# define TRUE true
+#elif ! TRUE
+# error TRUE must be true
+#endif
+
+#endif /* RUBY_BACKWARD2_BOOL_H */
diff --git a/include/ruby/backward/2/gcc_version_since.h b/include/ruby/backward/2/gcc_version_since.h
new file mode 100644
index 0000000000..00cc40ca56
--- /dev/null
+++ b/include/ruby/backward/2/gcc_version_since.h
@@ -0,0 +1,37 @@
+#ifndef RUBY_BACKWARD2_GCC_VERSION_SINCE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_GCC_VERSION_SINCE_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines old GCC_VERSION_SINCE
+ */
+#include "ruby/internal/compiler_since.h"
+
+#ifndef GCC_VERSION_SINCE
+#define GCC_VERSION_SINCE(x, y, z) RBIMPL_COMPILER_SINCE(GCC, (x), (y), (z))
+#endif
+
+#ifndef GCC_VERSION_BEFORE
+#define GCC_VERSION_BEFORE(x, y, z) \
+ (RBIMPL_COMPILER_BEFORE(GCC, (x), (y), (z)) || \
+ (RBIMPL_COMPILER_IS(GCC) && \
+ ((RBIMPL_COMPILER_VERSION_MAJOR == (x)) && \
+ ((RBIMPL_COMPILER_VERSION_MINOR == (y)) && \
+ (RBIMPL_COMPILER_VERSION_PATCH == (z))))))
+#endif
+
+#endif /* RUBY_BACKWARD2_GCC_VERSION_SINCE_H */
diff --git a/include/ruby/backward/2/inttypes.h b/include/ruby/backward/2/inttypes.h
new file mode 100644
index 0000000000..45460878bc
--- /dev/null
+++ b/include/ruby/backward/2/inttypes.h
@@ -0,0 +1,131 @@
+#ifndef RUBY_BACKWARD2_INTTYPES_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_INTTYPES_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief C99 shim for `<inttypes.h>`
+ */
+#include "ruby/internal/config.h" /* PRI_LL_PREFIX etc. are here */
+
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#include "ruby/internal/value.h" /* PRI_VALUE_PREFIX is here. */
+
+#ifndef PRI_INT_PREFIX
+# define PRI_INT_PREFIX ""
+#endif
+
+#ifndef PRI_LONG_PREFIX
+# define PRI_LONG_PREFIX "l"
+#endif
+
+#ifndef PRI_SHORT_PREFIX
+# define PRI_SHORT_PREFIX "h"
+#endif
+
+#ifdef PRI_64_PREFIX
+# /* Take that. */
+#elif SIZEOF_LONG == 8
+# define PRI_64_PREFIX PRI_LONG_PREFIX
+#elif SIZEOF_LONG_LONG == 8
+# define PRI_64_PREFIX PRI_LL_PREFIX
+#endif
+
+#ifndef PRIdPTR
+# define PRIdPTR PRI_PTR_PREFIX"d"
+# define PRIiPTR PRI_PTR_PREFIX"i"
+# define PRIoPTR PRI_PTR_PREFIX"o"
+# define PRIuPTR PRI_PTR_PREFIX"u"
+# define PRIxPTR PRI_PTR_PREFIX"x"
+# define PRIXPTR PRI_PTR_PREFIX"X"
+#endif
+
+#ifndef RUBY_PRI_VALUE_MARK
+# define RUBY_PRI_VALUE_MARK "\v"
+#endif
+
+#if defined PRIdPTR && !defined PRI_VALUE_PREFIX
+# define PRIdVALUE PRIdPTR
+# define PRIoVALUE PRIoPTR
+# define PRIuVALUE PRIuPTR
+# define PRIxVALUE PRIxPTR
+# define PRIXVALUE PRIXPTR
+# define PRIsVALUE PRIiPTR"" RUBY_PRI_VALUE_MARK
+#else
+# define PRIdVALUE PRI_VALUE_PREFIX"d"
+# define PRIoVALUE PRI_VALUE_PREFIX"o"
+# define PRIuVALUE PRI_VALUE_PREFIX"u"
+# define PRIxVALUE PRI_VALUE_PREFIX"x"
+# define PRIXVALUE PRI_VALUE_PREFIX"X"
+# define PRIsVALUE PRI_VALUE_PREFIX"i" RUBY_PRI_VALUE_MARK
+#endif
+
+#ifndef PRI_VALUE_PREFIX
+# define PRI_VALUE_PREFIX ""
+#endif
+
+#ifdef PRI_TIMET_PREFIX
+# /* Take that. */
+#elif SIZEOF_TIME_T == SIZEOF_INT
+# define PRI_TIMET_PREFIX
+#elif SIZEOF_TIME_T == SIZEOF_LONG
+# define PRI_TIMET_PREFIX "l"
+#elif SIZEOF_TIME_T == SIZEOF_LONG_LONG
+# define PRI_TIMET_PREFIX PRI_LL_PREFIX
+#endif
+
+#ifdef PRI_PTRDIFF_PREFIX
+# /* Take that. */
+#elif SIZEOF_PTRDIFF_T == SIZEOF_INT
+# define PRI_PTRDIFF_PREFIX ""
+#elif SIZEOF_PTRDIFF_T == SIZEOF_LONG
+# define PRI_PTRDIFF_PREFIX "l"
+#elif SIZEOF_PTRDIFF_T == SIZEOF_LONG_LONG
+# define PRI_PTRDIFF_PREFIX PRI_LL_PREFIX
+#endif
+
+#ifndef PRIdPTRDIFF
+# define PRIdPTRDIFF PRI_PTRDIFF_PREFIX"d"
+# define PRIiPTRDIFF PRI_PTRDIFF_PREFIX"i"
+# define PRIoPTRDIFF PRI_PTRDIFF_PREFIX"o"
+# define PRIuPTRDIFF PRI_PTRDIFF_PREFIX"u"
+# define PRIxPTRDIFF PRI_PTRDIFF_PREFIX"x"
+# define PRIXPTRDIFF PRI_PTRDIFF_PREFIX"X"
+#endif
+
+#ifdef PRI_SIZE_PREFIX
+# /* Take that. */
+#elif SIZEOF_SIZE_T == SIZEOF_INT
+# define PRI_SIZE_PREFIX ""
+#elif SIZEOF_SIZE_T == SIZEOF_LONG
+# define PRI_SIZE_PREFIX "l"
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
+# define PRI_SIZE_PREFIX PRI_LL_PREFIX
+#endif
+
+#ifndef PRIdSIZE
+# define PRIdSIZE PRI_SIZE_PREFIX"d"
+# define PRIiSIZE PRI_SIZE_PREFIX"i"
+# define PRIoSIZE PRI_SIZE_PREFIX"o"
+# define PRIuSIZE PRI_SIZE_PREFIX"u"
+# define PRIxSIZE PRI_SIZE_PREFIX"x"
+# define PRIXSIZE PRI_SIZE_PREFIX"X"
+#endif
+
+#endif /* RUBY_BACKWARD2_INTTYPES_H */
diff --git a/include/ruby/backward/2/limits.h b/include/ruby/backward/2/limits.h
new file mode 100644
index 0000000000..6f7021e5f4
--- /dev/null
+++ b/include/ruby/backward/2/limits.h
@@ -0,0 +1,99 @@
+#ifndef RUBY_BACKWARD2_LIMITS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_LIMITS_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Historical shim for `<limits.h>`.
+ *
+ * The macros in this header file are obsolescent. Does anyone really need our
+ * own definition of `CHAR_BIT` today?
+ */
+#include "ruby/internal/config.h"
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#include "ruby/backward/2/long_long.h"
+
+#ifndef LONG_MAX
+# /* assuming 32bit(2's complement) long */
+# define LONG_MAX 2147483647L
+#endif
+
+#ifndef LONG_MIN
+# define LONG_MIN (-LONG_MAX-1)
+#endif
+
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+
+#ifdef LLONG_MAX
+# /* Take that. */
+#elif defined(LONG_LONG_MAX)
+# define LLONG_MAX LONG_LONG_MAX
+#elif defined(_I64_MAX)
+# define LLONG_MAX _I64_MAX
+#else
+# /* assuming 64bit(2's complement) long long */
+# define LLONG_MAX 9223372036854775807LL
+#endif
+
+#ifdef LLONG_MIN
+# /* Take that. */
+#elif defined(LONG_LONG_MIN)
+# define LLONG_MIN LONG_LONG_MIN
+#elif defined(_I64_MAX)
+# define LLONG_MIN _I64_MIN
+#else
+# define LLONG_MIN (-LLONG_MAX-1)
+#endif
+
+#ifdef SIZE_MAX
+# /* Take that. */
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
+# define SIZE_MAX ULLONG_MAX
+# define SIZE_MIN ULLONG_MIN
+#elif SIZEOF_SIZE_T == SIZEOF_LONG
+# define SIZE_MAX ULONG_MAX
+# define SIZE_MIN ULONG_MIN
+#elif SIZEOF_SIZE_T == SIZEOF_INT
+# define SIZE_MAX UINT_MAX
+# define SIZE_MIN UINT_MIN
+#else
+# define SIZE_MAX USHRT_MAX
+# define SIZE_MIN USHRT_MIN
+#endif
+
+#ifdef SSIZE_MAX
+# /* Take that. */
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
+# define SSIZE_MAX LLONG_MAX
+# define SSIZE_MIN LLONG_MIN
+#elif SIZEOF_SIZE_T == SIZEOF_LONG
+# define SSIZE_MAX LONG_MAX
+# define SSIZE_MIN LONG_MIN
+#elif SIZEOF_SIZE_T == SIZEOF_INT
+# define SSIZE_MAX INT_MAX
+# define SSIZE_MIN INT_MIN
+#else
+# define SSIZE_MAX SHRT_MAX
+# define SSIZE_MIN SHRT_MIN
+#endif
+
+#endif /* RUBY_BACKWARD2_LIMITS_H */
diff --git a/include/ruby/backward/2/long_long.h b/include/ruby/backward/2/long_long.h
new file mode 100644
index 0000000000..8e6b2743fc
--- /dev/null
+++ b/include/ruby/backward/2/long_long.h
@@ -0,0 +1,73 @@
+#ifndef RUBY_BACKWARD2_LONG_LONG_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_LONG_LONG_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines old #LONG_LONG
+ *
+ * No known compiler that can compile today's ruby lacks long long.
+ * Historically MSVC was one of such compiler, but it implemented long long a
+ * while ago (some time back in 2013). The macros are for backwards
+ * compatibility only.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/has/warning.h"
+#include "ruby/internal/warning_push.h"
+
+#if defined(__DOXYGEN__)
+# /** @cond INTERNAL_MACRO */
+# define HAVE_LONG_LONG 1
+# define HAVE_TRUE_LONG_LONG 1
+# /** @endcond */
+# /** @deprecated Just use `long long` directly. */
+# define LONG_LONG long long.
+
+#elif RBIMPL_HAS_WARNING("-Wc++11-long-long")
+# define HAVE_TRUE_LONG_LONG 1
+# define LONG_LONG \
+ RBIMPL_WARNING_PUSH() \
+ RBIMPL_WARNING_IGNORED(-Wc++11-long-long) \
+ long long \
+ RBIMPL_WARNING_POP()
+
+#elif RBIMPL_HAS_WARNING("-Wlong-long")
+# define HAVE_TRUE_LONG_LONG 1
+# define LONG_LONG \
+ RBIMPL_WARNING_PUSH() \
+ RBIMPL_WARNING_IGNORED(-Wlong-long) \
+ long long \
+ RBIMPL_WARNING_POP()
+
+#elif defined(HAVE_LONG_LONG)
+# define HAVE_TRUE_LONG_LONG 1
+# define LONG_LONG long long
+
+#elif SIZEOF___INT64 > 0
+# define HAVE_LONG_LONG 1
+# define LONG_LONG __int64
+# undef SIZEOF_LONG_LONG
+# define SIZEOF_LONG_LONG SIZEOF___INT64
+
+#else
+# error Hello! Ruby developers believe this message must not happen.
+# error If you encounter this message, can you file a bug report?
+# error Remember to attach a detailed description of your environment.
+# error Thank you!
+#endif
+
+#endif /* RBIMPL_BACKWARD2_LONG_LONG_H */
diff --git a/include/ruby/backward/2/r_cast.h b/include/ruby/backward/2/r_cast.h
new file mode 100644
index 0000000000..3d0f40fd1e
--- /dev/null
+++ b/include/ruby/backward/2/r_cast.h
@@ -0,0 +1,32 @@
+#ifndef RUBY_BACKWARD2_R_CAST_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_R_CAST_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines old R_CAST
+ *
+ * Nobody is actively using this macro.
+ */
+#define R_CAST(st) (struct st*)
+#define RMOVED(obj) (R_CAST(RMoved)(obj))
+
+#if defined(__GNUC__)
+# warning R_CAST and RMOVED are deprecated
+#elif defined(_MSC_VER)
+# pragma message("warning: R_CAST and RMOVED are deprecated")
+#endif
+#endif /* RUBY_BACKWARD2_R_CAST_H */
diff --git a/include/ruby/backward/2/rmodule.h b/include/ruby/backward/2/rmodule.h
new file mode 100644
index 0000000000..76c0936462
--- /dev/null
+++ b/include/ruby/backward/2/rmodule.h
@@ -0,0 +1,36 @@
+#ifndef RUBY_BACKWARD2_RMODULE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_RMODULE_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Orphan macros.
+ *
+ * These macros seems broken since at least 2011. Nobody (except ruby itself
+ * who is implementing the internals) could have used those macros for a while.
+ * Kept public as-is here to keep some theoretical backwards compatibility.
+ */
+#define RMODULE_IV_TBL(m) RCLASS_FIELDS(m)
+#define RMODULE_CONST_TBL(m) RCLASS_CONST_TBL(m)
+#define RMODULE_M_TBL(m) RCLASS_M_TBL(m)
+#define RMODULE_SUPER(m) RCLASS_SUPER(m)
+
+#if defined(__GNUC__)
+# warning RMODULE_* macros are deprecated
+#elif defined(_MSC_VER)
+# pragma message("warning: RMODULE_* macros are deprecated")
+#endif
+#endif /* RUBY_BACKWARD2_RMODULE_H */
diff --git a/include/ruby/backward/2/stdalign.h b/include/ruby/backward/2/stdalign.h
new file mode 100644
index 0000000000..8b491bf564
--- /dev/null
+++ b/include/ruby/backward/2/stdalign.h
@@ -0,0 +1,30 @@
+#ifndef RUBY_BACKWARD2_STDALIGN_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_STDALIGN_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RUBY_ALIGNAS / #RUBY_ALIGNOF
+ */
+#include "ruby/internal/stdalign.h"
+
+#undef RUBY_ALIGNAS
+#undef RUBY_ALIGNOF
+#define RUBY_ALIGNAS RBIMPL_ALIGNAS /**< @copydoc RBIMPL_ALIGNAS */
+#define RUBY_ALIGNOF RBIMPL_ALIGNOF /**< @copydoc RBIMPL_ALIGNOF */
+
+#endif /* RUBY_BACKWARD2_STDALIGN_H */
diff --git a/include/ruby/backward/2/stdarg.h b/include/ruby/backward/2/stdarg.h
new file mode 100644
index 0000000000..08659fee47
--- /dev/null
+++ b/include/ruby/backward/2/stdarg.h
@@ -0,0 +1,69 @@
+#ifndef RUBY_BACKWARD2_STDARG_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_BACKWARD2_STDARG_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines old #_
+ *
+ * Nobody should ever use these macros any longer. No known compilers lack
+ * prototypes today. It's 21st century. Just forget them.
+ */
+
+#undef _
+/**
+ * @deprecated Nobody practically needs this macro any longer.
+ * @brief This was a transition path from K&R to ANSI.
+ */
+#ifdef HAVE_PROTOTYPES
+# define _(args) args
+#else
+# define _(args) ()
+#endif
+
+#undef __
+/**
+ * @deprecated Nobody practically needs this macro any longer.
+ * @brief This was a transition path from K&R to ANSI.
+ */
+#ifdef HAVE_STDARG_PROTOTYPES
+# define __(args) args
+#else
+# define __(args) ()
+#endif
+
+/**
+ * Functions declared using this macro take arbitrary arguments, including
+ * void.
+ *
+ * ```CXX
+ * void func(ANYARGS);
+ * ```
+ *
+ * This was a necessary evil when there was no such thing like function
+ * overloading. But it is the 21st century today. People generally need not
+ * use this. Just use a granular typed function.
+ *
+ * @see ruby::backward::cxxanyargs
+ */
+#ifdef __cplusplus
+#define ANYARGS ...
+#else
+#define ANYARGS
+#endif
+
+#endif /* RUBY_BACKWARD2_STDARG_H */
diff --git a/include/ruby/backward/cxxanyargs.hpp b/include/ruby/backward/cxxanyargs.hpp
new file mode 100644
index 0000000000..0ca2745c20
--- /dev/null
+++ b/include/ruby/backward/cxxanyargs.hpp
@@ -0,0 +1,671 @@
+#ifndef RUBY_BACKWARD_CXXANYARGS_HPP //-*-C++-*-vi:ft=cpp
+#define RUBY_BACKWARD_CXXANYARGS_HPP
+/// @file
+/// @author @shyouhei
+/// @copyright This file is a part of the programming language Ruby.
+/// Permission is hereby granted, to either redistribute and/or
+/// modify this file, provided that the conditions mentioned in the
+/// file COPYING are met. Consult the file for details.
+/// @note DO NOT MODERNISE THIS FILE! As the file name implies it is
+/// meant to be a backwards compatibility shim. Please stick to
+/// C++ 98 and never use newer features, like `constexpr`.
+/// @brief Provides old prototypes for C++ programs.
+#include "ruby/internal/config.h"
+#include "ruby/internal/intern/class.h"
+#include "ruby/internal/intern/cont.h"
+#include "ruby/internal/intern/hash.h"
+#include "ruby/internal/intern/proc.h"
+#include "ruby/internal/intern/thread.h"
+#include "ruby/internal/intern/variable.h"
+#include "ruby/internal/intern/vm.h"
+#include "ruby/internal/iterator.h"
+#include "ruby/internal/method.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/variable.h"
+#include "ruby/backward/2/stdarg.h"
+#include "ruby/st.h"
+
+extern "C++" {
+
+#ifdef HAVE_NULLPTR
+#include <cstddef>
+#endif
+
+/// @brief The main namespace.
+/// @note The name "ruby" might already be taken, but that must not be a
+/// problem because namespaces are allowed to reopen.
+namespace ruby {
+
+/// Backwards compatibility layer.
+namespace backward {
+
+/// Provides ANYARGS deprecation warnings. In C, ANYARGS means there is no
+/// function prototype. Literally anything, even including nothing, can be a
+/// valid ANYARGS. So passing a correctly prototyped function pointer to an
+/// ANYARGS-ed function parameter is valid, at the same time passing an
+/// ANYARGS-ed function pointer to a granular typed function parameter is also
+/// valid. However on the other hand in C++, ANYARGS doesn't actually mean any
+/// number of arguments. C++'s ANYARGS means _variadic_ number of arguments.
+/// This is incompatible with ordinal, correct function prototypes.
+///
+/// Luckily, function prototypes being distinct each other means they can be
+/// overloaded. We can provide a compatibility layer for older Ruby APIs which
+/// used to have ANYARGS. This namespace includes such attempts.
+namespace cxxanyargs {
+
+typedef VALUE type(ANYARGS); ///< ANYARGS-ed function type.
+typedef void void_type(ANYARGS); ///< ANYARGS-ed function type, void variant.
+typedef int int_type(ANYARGS); ///< ANYARGS-ed function type, int variant.
+typedef VALUE onearg_type(VALUE); ///< Single-argumented function type.
+
+/// @name Hooking global variables
+/// @{
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Define a function-backended global variable.
+/// @param[in] q Name of the variable.
+/// @param[in] w Getter function.
+/// @param[in] e Setter function.
+/// @note Both functions can be nullptr.
+/// @see rb_define_hooked_variable()
+/// @deprecated Use granular typed overload instead.
+inline void
+rb_define_virtual_variable(const char *q, type *w, void_type *e)
+{
+ rb_gvar_getter_t *r = reinterpret_cast<rb_gvar_getter_t*>(w);
+ rb_gvar_setter_t *t = reinterpret_cast<rb_gvar_setter_t*>(e);
+ ::rb_define_virtual_variable(q, r, t);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+inline void
+rb_define_virtual_variable(const char *q, rb_gvar_getter_t *w, void_type *e)
+{
+ rb_gvar_setter_t *t = reinterpret_cast<rb_gvar_setter_t*>(e);
+ ::rb_define_virtual_variable(q, w, t);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+inline void
+rb_define_virtual_variable(const char *q, type *w, rb_gvar_setter_t *e)
+{
+ rb_gvar_getter_t *r = reinterpret_cast<rb_gvar_getter_t*>(w);
+ ::rb_define_virtual_variable(q, r, e);
+}
+
+#ifdef HAVE_NULLPTR
+inline void
+rb_define_virtual_variable(const char *q, rb_gvar_getter_t *w, std::nullptr_t e)
+{
+ ::rb_define_virtual_variable(q, w, e);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+inline void
+rb_define_virtual_variable(const char *q, type *w, std::nullptr_t e)
+{
+ rb_gvar_getter_t *r = reinterpret_cast<rb_gvar_getter_t *>(w);
+ ::rb_define_virtual_variable(q, r, e);
+}
+
+inline void
+rb_define_virtual_variable(const char *q, std::nullptr_t w, rb_gvar_setter_t *e)
+{
+ ::rb_define_virtual_variable(q, w, e);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+inline void
+rb_define_virtual_variable(const char *q, std::nullptr_t w, void_type *e)
+{
+ rb_gvar_setter_t *r = reinterpret_cast<rb_gvar_setter_t *>(e);
+ ::rb_define_virtual_variable(q, w, r);
+}
+#endif
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Define a function-backended global variable.
+/// @param[in] q Name of the variable.
+/// @param[in] w Variable storage.
+/// @param[in] e Getter function.
+/// @param[in] r Setter function.
+/// @note Both functions can be nullptr.
+/// @see rb_define_virtual_variable()
+/// @deprecated Use granular typed overload instead.
+inline void
+rb_define_hooked_variable(const char *q, VALUE *w, type *e, void_type *r)
+{
+ rb_gvar_getter_t *t = reinterpret_cast<rb_gvar_getter_t*>(e);
+ rb_gvar_setter_t *y = reinterpret_cast<rb_gvar_setter_t*>(r);
+ ::rb_define_hooked_variable(q, w, t, y);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+inline void
+rb_define_hooked_variable(const char *q, VALUE *w, rb_gvar_getter_t *e, void_type *r)
+{
+ rb_gvar_setter_t *y = reinterpret_cast<rb_gvar_setter_t*>(r);
+ ::rb_define_hooked_variable(q, w, e, y);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+inline void
+rb_define_hooked_variable(const char *q, VALUE *w, type *e, rb_gvar_setter_t *r)
+{
+ rb_gvar_getter_t *t = reinterpret_cast<rb_gvar_getter_t*>(e);
+ ::rb_define_hooked_variable(q, w, t, r);
+}
+
+#ifdef HAVE_NULLPTR
+inline void
+rb_define_hooked_variable(const char *q, VALUE *w, rb_gvar_getter_t *e, std::nullptr_t r)
+{
+ ::rb_define_hooked_variable(q, w, e, r);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+inline void
+rb_define_hooked_variable(const char *q, VALUE *w, type *e, std::nullptr_t r)
+{
+ rb_gvar_getter_t *y = reinterpret_cast<rb_gvar_getter_t *>(e);
+ ::rb_define_hooked_variable(q, w, y, r);
+}
+
+inline void
+rb_define_hooked_variable(const char *q, VALUE *w, std::nullptr_t e, rb_gvar_setter_t *r)
+{
+ ::rb_define_hooked_variable(q, w, e, r);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+inline void
+rb_define_hooked_variable(const char *q, VALUE *w, std::nullptr_t e, void_type *r)
+{
+ rb_gvar_setter_t *y = reinterpret_cast<rb_gvar_setter_t *>(r);
+ ::rb_define_hooked_variable(q, w, e, y);
+}
+#endif
+
+/// @}
+/// @name Exceptions and tag jumps
+/// @{
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Call a method with a block.
+/// @param[in] q The self.
+/// @param[in] w The method.
+/// @param[in] e The # of elems of `r`
+/// @param[in] r The arguments.
+/// @param[in] t What is to be yielded.
+/// @param[in] y Passed to `t`
+/// @return Return value of `q#w(*r,&t)`
+/// @note 't' can be nullptr.
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_block_call(VALUE q, ID w, int e, const VALUE *r, type *t, VALUE y)
+{
+ rb_block_call_func_t u = reinterpret_cast<rb_block_call_func_t>(t);
+ return ::rb_block_call(q, w, e, r, u, y);
+}
+
+#ifdef HAVE_NULLPTR
+inline VALUE
+rb_block_call(VALUE q, ID w, int e, const VALUE *r, std::nullptr_t t, VALUE y)
+{
+ return ::rb_block_call(q, w, e, r, t, y);
+}
+#endif
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief An equivalent of `rescue` clause.
+/// @param[in] q A function that can raise.
+/// @param[in] w Passed to `q`.
+/// @param[in] e A function that cleans-up.
+/// @param[in] r Passed to `e`.
+/// @return The return value of `q` if no exception occurs, or the return
+/// value of `e` if otherwise.
+/// @note `e` can be nullptr.
+/// @see rb_ensure()
+/// @see rb_rescue2()
+/// @see rb_protect()
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_rescue(type *q, VALUE w, type *e, VALUE r)
+{
+ typedef VALUE func1_t(VALUE);
+ typedef VALUE func2_t(VALUE, VALUE);
+ func1_t *t = reinterpret_cast<func1_t*>(q);
+ func2_t *y = reinterpret_cast<func2_t*>(e);
+ return ::rb_rescue(t, w, y, r);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief An equivalent of `rescue` clause.
+/// @param[in] q A function that can raise.
+/// @param[in] w Passed to `q`.
+/// @param[in] e A function that cleans-up.
+/// @param[in] r Passed to `e`.
+/// @param[in] ... 0-terminated list of subclass of @ref rb_eException.
+/// @return The return value of `q` if no exception occurs, or the return
+/// value of `e` if otherwise.
+/// @note `e` can be nullptr.
+/// @see rb_ensure()
+/// @see rb_rescue()
+/// @see rb_protect()
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_rescue2(type *q, VALUE w, type *e, VALUE r, ...)
+{
+ typedef VALUE func1_t(VALUE);
+ typedef VALUE func2_t(VALUE, VALUE);
+ func1_t *t = reinterpret_cast<func1_t*>(q);
+ func2_t *y = reinterpret_cast<func2_t*>(e);
+ va_list ap;
+ va_start(ap, r);
+ VALUE ret = ::rb_vrescue2(t, w, y, r, ap);
+ va_end(ap);
+ return ret;
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief An equivalent of `ensure` clause.
+/// @param[in] q A function that can raise.
+/// @param[in] w Passed to `q`.
+/// @param[in] e A function that ensures.
+/// @param[in] r Passed to `e`.
+/// @return The return value of `q`.
+/// @note It makes no sense to pass nullptr to `e`.
+/// @see rb_rescue()
+/// @see rb_rescue2()
+/// @see rb_protect()
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_ensure(type *q, VALUE w, type *e, VALUE r)
+{
+ typedef VALUE func1_t(VALUE);
+ func1_t *t = reinterpret_cast<func1_t*>(q);
+ func1_t *y = reinterpret_cast<func1_t*>(e);
+ return ::rb_ensure(t, w, y, r);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief An equivalent of `Kernel#catch`.
+/// @param[in] q The "tag" string.
+/// @param[in] w A function that can throw.
+/// @param[in] e Passed to `w`.
+/// @return What was thrown.
+/// @note `q` can be a nullptr but makes no sense to pass nullptr to`w`.
+/// @see rb_block_call()
+/// @see rb_protect()
+/// @see rb_rb_catch_obj()
+/// @see rb_rescue()
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_catch(const char *q, type *w, VALUE e)
+{
+ rb_block_call_func_t r = reinterpret_cast<rb_block_call_func_t>(w);
+ return ::rb_catch(q, r, e);
+}
+
+#ifdef HAVE_NULLPTR
+inline VALUE
+rb_catch(const char *q, std::nullptr_t w, VALUE e)
+{
+ return ::rb_catch(q, w, e);
+}
+#endif
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief An equivalent of `Kernel#catch`.
+/// @param[in] q The "tag" object.
+/// @param[in] w A function that can throw.
+/// @param[in] e Passed to `w`.
+/// @return What was thrown.
+/// @note It makes no sense to pass nullptr to`w`.
+/// @see rb_block_call()
+/// @see rb_protect()
+/// @see rb_rb_catch_obj()
+/// @see rb_rescue()
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_catch_obj(VALUE q, type *w, VALUE e)
+{
+ rb_block_call_func_t r = reinterpret_cast<rb_block_call_func_t>(w);
+ return ::rb_catch_obj(q, r, e);
+}
+
+/// @}
+/// @name Procs, Fibers and Threads
+/// @{
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Creates a rb_cFiber instance.
+/// @param[in] q The fiber body.
+/// @param[in] w Passed to `q`.
+/// @return What was allocated.
+/// @note It makes no sense to pass nullptr to`q`.
+/// @see rb_proc_new()
+/// @see rb_thread_create()
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_fiber_new(type *q, VALUE w)
+{
+ rb_block_call_func_t e = reinterpret_cast<rb_block_call_func_t>(q);
+ return ::rb_fiber_new(e, w);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Creates a @ref rb_cProc instance.
+/// @param[in] q The proc body.
+/// @param[in] w Passed to `q`.
+/// @return What was allocated.
+/// @note It makes no sense to pass nullptr to`q`.
+/// @see rb_fiber_new()
+/// @see rb_thread_create()
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_proc_new(type *q, VALUE w)
+{
+ rb_block_call_func_t e = reinterpret_cast<rb_block_call_func_t>(q);
+ return ::rb_proc_new(e, w);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Creates a @ref rb_cThread instance.
+/// @param[in] q The thread body.
+/// @param[in] w Passed to `q`.
+/// @return What was allocated.
+/// @note It makes no sense to pass nullptr to`q`.
+/// @see rb_proc_new()
+/// @see rb_fiber_new()
+/// @deprecated Use granular typed overload instead.
+inline VALUE
+rb_thread_create(type *q, void *w)
+{
+ typedef VALUE ptr_t(void*);
+ ptr_t *e = reinterpret_cast<ptr_t*>(q);
+ return ::rb_thread_create(e, w);
+}
+
+/// @}
+/// @name Hash and st_table
+/// @{
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Iteration over the given table.
+/// @param[in] q A table to scan.
+/// @param[in] w A function to iterate.
+/// @param[in] e Passed to `w`.
+/// @retval 0 Always returns 0.
+/// @note It makes no sense to pass nullptr to`w`.
+/// @see st_foreach_check()
+/// @see rb_hash_foreach()
+/// @deprecated Use granular typed overload instead.
+inline int
+st_foreach(st_table *q, int_type *w, st_data_t e)
+{
+ st_foreach_callback_func *r =
+ reinterpret_cast<st_foreach_callback_func*>(w);
+ return ::st_foreach(q, r, e);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Iteration over the given table.
+/// @param[in] q A table to scan.
+/// @param[in] w A function to iterate.
+/// @param[in] e Passed to `w`.
+/// @retval 0 Successful end of iteration.
+/// @retval 1 Element removed during traversing.
+/// @note It makes no sense to pass nullptr to`w`.
+/// @see st_foreach()
+/// @deprecated Use granular typed overload instead.
+inline int
+st_foreach_check(st_table *q, int_type *w, st_data_t e, st_data_t)
+{
+ st_foreach_check_callback_func *t =
+ reinterpret_cast<st_foreach_check_callback_func*>(w);
+ return ::st_foreach_check(q, t, e, 0);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Iteration over the given table.
+/// @param[in] q A table to scan.
+/// @param[in] w A function to iterate.
+/// @param[in] e Passed to `w`.
+/// @note It makes no sense to pass nullptr to`w`.
+/// @see st_foreach_check()
+/// @deprecated Use granular typed overload instead.
+inline void
+st_foreach_safe(st_table *q, int_type *w, st_data_t e)
+{
+ st_foreach_callback_func *r =
+ reinterpret_cast<st_foreach_callback_func*>(w);
+ ::st_foreach_safe(q, r, e);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Iteration over the given hash.
+/// @param[in] q A hash to scan.
+/// @param[in] w A function to iterate.
+/// @param[in] e Passed to `w`.
+/// @note It makes no sense to pass nullptr to`w`.
+/// @see st_foreach()
+/// @deprecated Use granular typed overload instead.
+inline void
+rb_hash_foreach(VALUE q, int_type *w, VALUE e)
+{
+ st_foreach_callback_func *r =
+ reinterpret_cast<st_foreach_callback_func*>(w);
+ ::rb_hash_foreach(q, r, e);
+}
+
+RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated")
+/// @brief Iteration over each instance variable of the object.
+/// @param[in] q An object.
+/// @param[in] w A function to iterate.
+/// @param[in] e Passed to `w`.
+/// @note It makes no sense to pass nullptr to`w`.
+/// @see st_foreach()
+/// @deprecated Use granular typed overload instead.
+inline void
+rb_ivar_foreach(VALUE q, int_type *w, VALUE e)
+{
+ st_foreach_callback_func *r =
+ reinterpret_cast<st_foreach_callback_func*>(w);
+ ::rb_ivar_foreach(q, r, e);
+}
+
+/// @}
+
+/// Driver for *_define_method. ::rb_define_method function for instance takes
+/// a pointer to ANYARGS-ed functions, which in fact varies 18 different
+/// prototypes. We still need to preserve ANYARGS for storages but why not
+/// check the consistencies if possible. In C++ a function has its own
+/// prototype, which is a compile-time constant (static type) by nature. We
+/// can list up all the possible input types and provide warnings for other
+/// cases. This is such attempt.
+namespace define_method {
+
+/// Type of ::rb_f_notimplement().
+typedef VALUE notimpl_type(int, const VALUE *, VALUE, VALUE);
+
+/// @brief Template metaprogramming to generate function prototypes.
+/// @tparam T Type of method id (`ID` or `const char*` in practice).
+/// @tparam F Definition driver e.g. ::rb_define_method.
+template<typename T, void (*F)(VALUE klass, T mid, type *func, int arity)>
+struct driver {
+
+ /// @brief Defines a method
+ /// @tparam N Arity of the function.
+ /// @tparam U The function in question
+ template<int N, typename U>
+ struct engine {
+
+ /* :TODO: Following deprecation attribute renders tons of warnings (one
+ * per every method definitions), which is annoying. Of course
+ * annoyance is the core feature of deprecation warnings... But that
+ * could be too much, especially when the warnings happen inside of
+ * machine-generated programs. And SWIG is known to do such thing.
+ * The new (granular) API was introduced in API version 2.7. As of
+ * this writing the version is 2.8. Let's warn this later, some time
+ * during 3.x. Hopefully codes in old (ANYARGS-ed) format should be
+ * less than now. */
+ RUBY_CXX_DEPRECATED("use of ANYARGS is deprecated")
+ /// @copydoc define(VALUE klass, T mid, U func)
+ /// @deprecated Pass correctly typed function instead.
+ static inline void
+ define(VALUE klass, T mid, type func)
+ {
+ F(klass, mid, func, N);
+ }
+
+ /// @brief Defines klass#mid as func, whose arity is N.
+ /// @param[in] klass Where the method lives.
+ /// @param[in] mid Name of the method to define.
+ /// @param[in] func Function that implements klass#mid.
+ static inline void
+ define(VALUE klass, T mid, U func)
+ {
+ F(klass, mid, reinterpret_cast<type *>(func), N);
+ }
+
+ /// @copydoc define(VALUE klass, T mid, U func)
+ static inline void
+ define(VALUE klass, T mid, notimpl_type func)
+ {
+ F(klass, mid, reinterpret_cast<type *>(func), N);
+ }
+ };
+
+ /// @cond INTERNAL_MACRO
+ template<int N, bool = false> struct specific : public engine<N, type *> {};
+ template<bool b> struct specific<15, b> : public engine<15, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<14, b> : public engine<14, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<13, b> : public engine<13, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<12, b> : public engine<12, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<11, b> : public engine<11, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<10, b> : public engine<10, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 9, b> : public engine< 9, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 8, b> : public engine< 8, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 7, b> : public engine< 7, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 6, b> : public engine< 6, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 5, b> : public engine< 5, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 4, b> : public engine< 4, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 3, b> : public engine< 3, VALUE(*)(VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 2, b> : public engine< 2, VALUE(*)(VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 1, b> : public engine< 1, VALUE(*)(VALUE, VALUE)> {};
+ template<bool b> struct specific< 0, b> : public engine< 0, VALUE(*)(VALUE)> {};
+ template<bool b> struct specific<-1, b> : public engine<-1, VALUE(*)(int argc, VALUE *argv, VALUE self)> {
+ using engine<-1, VALUE(*)(int argc, VALUE *argv, VALUE self)>::define;
+ static inline void define(VALUE c, T m, VALUE(*f)(int argc, const VALUE *argv, VALUE self)) { F(c, m, reinterpret_cast<type *>(f), -1); }
+ };
+ template<bool b> struct specific<-2, b> : public engine<-2, VALUE(*)(VALUE, VALUE)> {};
+ /// @endcond
+};
+
+/* We could perhaps merge this struct into the one above using variadic
+ * template parameters if we could assume C++11, but sadly we cannot. */
+/// @copydoc ruby::backward::cxxanyargs::define_method::driver
+template<typename T, void (*F)(T mid, type func, int arity)>
+struct driver0 {
+
+ /// @brief Defines a method
+ /// @tparam N Arity of the function.
+ /// @tparam U The function in question
+ template<int N, typename U>
+ struct engine {
+ RUBY_CXX_DEPRECATED("use of ANYARGS is deprecated")
+ /// @copydoc define(T mid, U func)
+ /// @deprecated Pass correctly typed function instead.
+ static inline void
+ define(T mid, type func)
+ {
+ F(mid, func, N);
+ }
+
+ /// @brief Defines Kernel#mid as func, whose arity is N.
+ /// @param[in] mid Name of the method to define.
+ /// @param[in] func Function that implements klass#mid.
+ static inline void
+ define(T mid, U func)
+ {
+ F(mid, reinterpret_cast<type *>(func), N);
+ }
+
+ /// @copydoc define(T mid, U func)
+ /// @deprecated Pass correctly typed function instead.
+ static inline void
+ define(T mid, notimpl_type func)
+ {
+ F(mid, reinterpret_cast<type *>(func), N);
+ }
+ };
+
+ /// @cond INTERNAL_MACRO
+ template<int N, bool = false> struct specific : public engine<N, type *> {};
+ template<bool b> struct specific<15, b> : public engine<15, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<14, b> : public engine<14, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<13, b> : public engine<13, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<12, b> : public engine<12, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<11, b> : public engine<11, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific<10, b> : public engine<10, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 9, b> : public engine< 9, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 8, b> : public engine< 8, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 7, b> : public engine< 7, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 6, b> : public engine< 6, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 5, b> : public engine< 5, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 4, b> : public engine< 4, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 3, b> : public engine< 3, VALUE(*)(VALUE, VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 2, b> : public engine< 2, VALUE(*)(VALUE, VALUE, VALUE)> {};
+ template<bool b> struct specific< 1, b> : public engine< 1, VALUE(*)(VALUE, VALUE)> {};
+ template<bool b> struct specific< 0, b> : public engine< 0, VALUE(*)(VALUE)> {};
+ template<bool b> struct specific<-1, b> : public engine<-1, VALUE(*)(int argc, VALUE *argv, VALUE self)> {
+ using engine<-1, VALUE(*)(int argc, VALUE *argv, VALUE self)>::define;
+ static inline void define(T m, VALUE(*f)(int argc, const VALUE *argv, VALUE self)) { F(m, reinterpret_cast<type *>(f), -1); }
+ };
+ template<bool b> struct specific<-2, b> : public engine<-2, VALUE(*)(VALUE, VALUE)> {};
+ /// @endcond
+};
+
+struct rb_define_method : public driver <const char *, ::rb_define_method> {}; ///< Dispatches appropriate driver for ::rb_define_method.
+struct rb_define_method_id : public driver <ID, ::rb_define_method_id> {}; ///< Dispatches appropriate driver for ::rb_define_method_id.
+struct rb_define_private_method : public driver <const char *, ::rb_define_private_method> {}; ///< Dispatches appropriate driver for ::rb_define_private_method.
+struct rb_define_protected_method : public driver <const char *, ::rb_define_protected_method> {}; ///< Dispatches appropriate driver for ::rb_define_protected_method.
+struct rb_define_singleton_method : public driver <const char *, ::rb_define_singleton_method> {}; ///< Dispatches appropriate driver for ::rb_define_singleton_method.
+struct rb_define_module_function : public driver <const char *, ::rb_define_module_function> {}; ///< Dispatches appropriate driver for ::rb_define_module_function.
+struct rb_define_global_function : public driver0<const char *, ::rb_define_global_function> {}; ///< Dispatches appropriate driver for ::rb_define_global_function.
+
+/// @brief Defines klass\#mid.
+/// @param klass Where the method lives.
+/// @copydetails #rb_define_global_function(mid, func, arity)
+#define rb_define_method(klass, mid, func, arity) ::ruby::backward::cxxanyargs::define_method::rb_define_method::specific<arity>::define(klass, mid, func)
+
+/// @copydoc #rb_define_method(klass, mid, func, arity)
+#define rb_define_method_id(klass, mid, func, arity) ::ruby::backward::cxxanyargs::define_method::rb_define_method_id::specific<arity>::define(klass, mid, func)
+
+/// @brief Defines klass\#mid and makes it private.
+/// @copydetails #rb_define_method(klass, mid, func, arity)
+#define rb_define_private_method(klass, mid, func, arity) ::ruby::backward::cxxanyargs::define_method::rb_define_private_method::specific<arity>::define(klass, mid, func)
+
+/// @brief Defines klass\#mid and makes it protected.
+/// @copydetails #rb_define_method
+#define rb_define_protected_method(klass, mid, func, arity) ::ruby::backward::cxxanyargs::define_method::rb_define_protected_method::specific<arity>::define(klass, mid, func)
+
+/// @brief Defines klass.mid.(klass, mid, func, arity)
+/// @copydetails #rb_define_method
+#define rb_define_singleton_method(klass, mid, func, arity) ::ruby::backward::cxxanyargs::define_method::rb_define_singleton_method::specific<arity>::define(klass, mid, func)
+
+/// @brief Defines klass\#mid and makes it a module function.
+/// @copydetails #rb_define_method(klass, mid, func, arity)
+#define rb_define_module_function(klass, mid, func, arity) ::ruby::backward::cxxanyargs::define_method::rb_define_module_function::specific<arity>::define(klass, mid, func)
+
+/// @brief Defines ::rb_mKernel \#mid.
+/// @param mid Name of the defining method.
+/// @param func Implementation of \#mid.
+/// @param arity Arity of \#mid.
+#define rb_define_global_function(mid, func, arity) ::ruby::backward::cxxanyargs::define_method::rb_define_global_function::specific<arity>::define(mid, func)
+
+}}}}}
+
+using namespace ruby::backward::cxxanyargs;
+#endif // RUBY_BACKWARD_CXXANYARGS_HPP
diff --git a/include/ruby/debug.h b/include/ruby/debug.h
new file mode 100644
index 0000000000..547d5d94c4
--- /dev/null
+++ b/include/ruby/debug.h
@@ -0,0 +1,834 @@
+#ifndef RB_DEBUG_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RB_DEBUG_H 1
+/**
+ * @file
+ * @author $Author: ko1 $
+ * @date Tue Nov 20 20:35:08 2012
+ * @copyright Copyright (C) 2012 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/returns_nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/event.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* Note: This file contains experimental APIs. */
+/* APIs can be replaced at Ruby 2.0.1 or later */
+
+/**
+ * @name Frame-profiling APIs
+ *
+ * @{
+ */
+
+RBIMPL_ATTR_NONNULL((3))
+/**
+ * Queries mysterious "frame"s of the given range.
+ *
+ * The returned values are opaque backtrace pointers, which you are allowed to
+ * issue a very limited set of operations listed below. Don't call arbitrary
+ * ruby methods.
+ *
+ * @param[in] start Start position (0 means the topmost).
+ * @param[in] limit Number objects of `buff`.
+ * @param[out] buff Return buffer.
+ * @param[out] lines Return buffer.
+ * @return Number of objects filled into `buff`.
+ * @post `buff` is filled with backtrace pointers.
+ * @post `lines` is filled with `__LINE__` of each backtraces.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't like this abuse of ::VALUE. It should have been
+ * `const struct rb_callable_method_entry_struct *`.
+ */
+int rb_profile_frames(int start, int limit, VALUE *buff, int *lines);
+
+/**
+ * Queries mysterious "frame"s of the given range.
+ *
+ * A per-thread version of rb_profile_frames().
+ * Arguments and return values are the same with rb_profile_frames() with the
+ * exception of the first argument _thread_, which accepts the Thread to be
+ * profiled/queried.
+ *
+ * @param[in] thread The Ruby Thread to be profiled.
+ * @param[in] start Start position (0 means the topmost).
+ * @param[in] limit Number objects of `buff`.
+ * @param[out] buff Return buffer.
+ * @param[out] lines Return buffer.
+ * @return Number of objects filled into `buff`.
+ * @post `buff` is filled with backtrace pointers.
+ * @post `lines` is filled with `__LINE__` of each backtraces.
+ */
+int rb_profile_thread_frames(VALUE thread, int start, int limit, VALUE *buff, int *lines);
+
+/**
+ * Queries the path of the passed backtrace.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qnil The frame is implemented in C etc.
+ * @retval otherwise Where `frame` is running.
+ */
+VALUE rb_profile_frame_path(VALUE frame);
+
+/**
+ * Identical to rb_profile_frame_path(), except it tries to expand the
+ * returning path. In case the path is `require`-d from something else
+ * rb_profile_frame_path() can return relative paths. This one tries to avoid
+ * that.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval "<cfunc>" The frame is in C.
+ * @retval RUBY_Qnil Can't infer real path (inside of `eval` etc.).
+ * @retval otherwise Where `frame` is running.
+ */
+VALUE rb_profile_frame_absolute_path(VALUE frame);
+
+/**
+ * Queries human-readable "label" string. This is `"<main>"` for the toplevel,
+ * `"<compiled>"` for evaluated ones, method name for methods, class name for
+ * classes.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qnil Can't infer the label (C etc.).
+ * @retval "<main>" The frame is global toplevel.
+ * @retval "<compiled>" The frame is dynamic.
+ * @retval otherwise Label of the frame.
+ */
+VALUE rb_profile_frame_label(VALUE frame);
+
+/**
+ * Identical to rb_profile_frame_label(), except it does not "qualify" the
+ * result. Consider the following backtrace:
+ *
+ * ```ruby
+ * def bar
+ * caller_locations
+ * end
+ *
+ * def foo
+ * [1].map { bar }.first
+ * end
+ *
+ * obj = foo.first
+ * obj.label # => "block in foo"
+ * obj.base_label # => "foo"
+ * ```
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qnil Can't infer the label (C etc.).
+ * @retval "<main>" The frame is global toplevel.
+ * @retval "<compiled>" The frame is dynamic.
+ * @retval otherwise Base label of the frame.
+ */
+VALUE rb_profile_frame_base_label(VALUE frame);
+
+/**
+ * Identical to rb_profile_frame_label(), except it returns a qualified result.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qnil Can't infer the label (C etc.).
+ * @retval "<main>" The frame is global toplevel.
+ * @retval "<compiled>" The frame is dynamic.
+ * @retval otherwise Qualified label of the frame.
+ *
+ * @internal
+ *
+ * As of writing there is no way to obtain this return value from a Ruby
+ * script. This may change in future (it took 8 years and still no progress,
+ * though).
+ */
+VALUE rb_profile_frame_full_label(VALUE frame);
+
+/**
+ * Queries the first line of the method of the passed frame pointer. Can be
+ * handy when for instance a debugger want to display the frame in question.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qnil Can't infer the line (C etc.).
+ * @retval otherwise Line number of the method in question.
+ */
+VALUE rb_profile_frame_first_lineno(VALUE frame);
+
+/**
+ * Queries the class path of the method that the passed frame represents.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qnil Can't infer the class (global toplevel etc.).
+ * @retval otherwise Class path as in rb_class_path().
+ */
+VALUE rb_profile_frame_classpath(VALUE frame);
+
+/**
+ * Queries if the method of the passed frame is a singleton class.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qtrue It is a singleton method.
+ * @retval RUBY_Qfalse Otherwise (normal method/non-method).
+ */
+VALUE rb_profile_frame_singleton_method_p(VALUE frame);
+
+/**
+ * Queries the name of the method of the passed frame.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qnil The frame in question is not a method.
+ * @retval otherwise Name of the method of the frame.
+ */
+VALUE rb_profile_frame_method_name(VALUE frame);
+
+/**
+ * Identical to rb_profile_frame_method_name(), except it "qualifies" the
+ * return value with its defining class.
+ *
+ * @param[in] frame What rb_profile_frames() returned.
+ * @retval RUBY_Qnil The frame in question is not a method.
+ * @retval otherwise Qualified name of the method of the frame.
+ */
+VALUE rb_profile_frame_qualified_method_name(VALUE frame);
+
+/** @} */
+
+/**
+ * @name Debug inspector APIs
+ *
+ * @{
+ */
+
+/** Opaque struct representing a debug inspector. */
+typedef struct rb_debug_inspector_struct rb_debug_inspector_t;
+
+/**
+ * Type of the callback function passed to rb_debug_inspector_open().
+ * Inspection shall happen only inside of them. The passed pointers gets
+ * invalidated once after the callback returns.
+ *
+ * @param[in] dc A debug context.
+ * @param[in,out] data What was passed to rb_debug_inspector_open().
+ * @return What would be the return value of rb_debug_inspector_open().
+ */
+typedef VALUE (*rb_debug_inspector_func_t)(const rb_debug_inspector_t *dc, void *data);
+
+/**
+ * Prepares, executes, then cleans up a debug session.
+ *
+ * @param[in] func A callback to run inside of a debug session.
+ * @param[in,out] data Passed as-is to `func`.
+ * @return What was returned from `func`.
+ */
+VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data);
+
+/**
+ * Queries the backtrace object of the context. This is as if you call
+ * `caller_locations` at the point of debugger.
+ *
+ * @param[in] dc A debug context.
+ * @return An array of `Thread::Backtrace::Location` which represents the
+ * current point of execution at `dc`.
+
+ */
+VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
+
+/**
+ * Queries the current receiver of the passed context's upper frame.
+ *
+ * @param[in] dc A debug context.
+ * @param[in] index Index of the frame from top to bottom.
+ * @exception rb_eArgError `index` out of range.
+ * @return The current receiver at `index`-th frame.
+ */
+VALUE rb_debug_inspector_frame_self_get(const rb_debug_inspector_t *dc, long index);
+
+/**
+ * Queries the current class of the passed context's upper frame.
+ *
+ * @param[in] dc A debug context.
+ * @param[in] index Index of the frame from top to bottom.
+ * @exception rb_eArgError `index` out of range.
+ * @return The current class at `index`-th frame.
+ */
+VALUE rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index);
+
+/**
+ * Queries the binding of the passed context's upper frame.
+ *
+ * @param[in] dc A debug context.
+ * @param[in] index Index of the frame from top to bottom.
+ * @exception rb_eArgError `index` out of range.
+ * @return The binding at `index`-th frame.
+ */
+VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index);
+
+/**
+ * Queries the instruction sequence of the passed context's upper frame.
+ *
+ * @param[in] dc A debug context.
+ * @param[in] index Index of the frame from top to bottom.
+ * @exception rb_eArgError `index` out of range.
+ * @retval RUBY_Qnil `index`-th frame is not in Ruby (C etc.).
+ * @retval otherwise An instance of `RubyVM::InstructionSequence` which
+ * represents the instruction sequence at `index`-th
+ * frame.
+ */
+VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index);
+
+/**
+ * Queries the depth of the passed context's upper frame.
+ *
+ * Note that the depth is not same as the frame index because debug_inspector
+ * skips some special frames but the depth counts all frames.
+ *
+ * @param[in] dc A debug context.
+ * @param[in] index Index of the frame from top to bottom.
+ * @exception rb_eArgError `index` out of range.
+ * @retval The depth at `index`-th frame in Integer.
+ */
+VALUE rb_debug_inspector_frame_depth(const rb_debug_inspector_t *dc, long index);
+
+// A macro to recognize `rb_debug_inspector_frame_depth()` is available or not
+#define RB_DEBUG_INSPECTOR_FRAME_DEPTH(dc, index) rb_debug_inspector_frame_depth(dc, index)
+
+/**
+ * Return current frame depth.
+ *
+ * @retval The depth of the current frame in Integer.
+ */
+VALUE rb_debug_inspector_current_depth(void);
+
+/** @} */
+
+/**
+ * @name Old style set_trace_func APIs
+ *
+ * @{
+ */
+
+/* duplicated def of include/ruby/ruby.h */
+#include "ruby/internal/event.h"
+
+/**
+ * Identical to rb_remove_event_hook(), except it additionally takes the data
+ * argument. This extra argument is the same as that of rb_add_event_hook(),
+ * and this function removes the hook which matches both arguments at once.
+ *
+ * @param[in] func A callback.
+ * @param[in] data What to be passed to `func`.
+ * @return Number of deleted event hooks.
+ * @note As multiple events can share the same `func` it is quite
+ * possible for the return value to become more than one.
+ */
+int rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data);
+
+/**
+ * Identical to rb_add_event_hook(), except its effect is limited to the passed
+ * thread. Other threads are not affected by this.
+ *
+ * @param[in] thval An instance of ::rb_cThread.
+ * @param[in] func A callback.
+ * @param[in] events A set of events that `func` should run.
+ * @param[in] data Passed as-is to `func`.
+ * @exception rb_eTypeError `thval` is not a thread.
+ */
+void rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data);
+
+/**
+ * Identical to rb_remove_event_hook(), except it additionally takes a thread
+ * argument. This extra argument is the same as that of
+ * rb_thread_add_event_hook(), and this function removes the hook which matches
+ * both arguments at once.
+ *
+ * @param[in] thval An instance of ::rb_cThread.
+ * @param[in] func A callback.
+ * @exception rb_eTypeError `thval` is not a thread.
+ * @return Number of deleted event hooks.
+ * @note As multiple events can share the same `func` it is quite
+ * possible for the return value to become more than one.
+ */
+int rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func);
+
+/**
+ * Identical to rb_thread_remove_event_hook(), except it additionally takes the
+ * data argument. It can also be seen as a routine identical to
+ * rb_remove_event_hook_with_data(), except it additionally takes the thread.
+ * This function deletes hooks that satisfy all three criteria.
+ *
+ * @param[in] thval An instance of ::rb_cThread.
+ * @param[in] func A callback.
+ * @param[in] data What to be passed to `func`.
+ * @exception rb_eTypeError `thval` is not a thread.
+ * @return Number of deleted event hooks.
+ * @note As multiple events can share the same `func` it is quite
+ * possible for the return value to become more than one.
+ */
+int rb_thread_remove_event_hook_with_data(VALUE thval, rb_event_hook_func_t func, VALUE data);
+
+/** @} */
+
+/**
+ * @name TracePoint APIs
+ *
+ * @{
+ */
+
+/**
+ * Creates a tracepoint by registering a callback function for one or more
+ * tracepoint events. Once the tracepoint is created, you can use
+ * rb_tracepoint_enable to enable the tracepoint.
+ *
+ * @param[in] target_thread_not_supported_yet Meant for picking the
+ * thread in which the tracepoint is to be created.
+ * However, current implementation ignore this
+ * parameter, tracepoint is created for all threads.
+ * Simply specify Qnil.
+ * @param[in] events Event(s) to listen to.
+ * @param[in] func A callback function.
+ * @param[in,out] data Void pointer that will be passed to the callback
+ * function.
+ *
+ * When the callback function is called, it will be passed 2 parameters:
+ * 1. `VALUE tpval` - the TracePoint object from which trace args can be
+ * extracted.
+ * 1. `void *data` - A void pointer which helps to share scope with the
+ * callback function.
+ *
+ * It is important to note that you cannot register callbacks for normal events
+ * and internal events simultaneously because they are different purpose. You
+ * can use any Ruby APIs (calling methods and so on) on normal event hooks.
+ * However, in internal events, you can not use any Ruby APIs (even object
+ * creations). This is why we can't specify internal events by TracePoint
+ * directly. Limitations are MRI version specific.
+ *
+ * Example:
+ *
+ * ```CXX
+ * rb_tracepoint_new(
+ * Qnil,
+ * RUBY_INTERNAL_EVENT_NEWOBJ | RUBY_INTERNAL_EVENT_FREEOBJ,
+ * obj_event_i,
+ * data);
+ * ```
+ *
+ * In this example, a callback function `obj_event_i` will be registered for
+ * internal events #RUBY_INTERNAL_EVENT_NEWOBJ and
+ * #RUBY_INTERNAL_EVENT_FREEOBJ.
+ */
+VALUE rb_tracepoint_new(VALUE target_thread_not_supported_yet, rb_event_flag_t events, void (*func)(VALUE, void *), void *data);
+
+/**
+ * Starts (enables) trace(s) defined by the passed object. A TracePoint object
+ * does not immediately take effect on creation. You have to explicitly call
+ * this API.
+ *
+ * @param[in] tpval An instance of TracePoint.
+ * @exception rb_eArgError A trace is already running.
+ * @return Undefined value. Forget this. It should have returned `void`.
+ * @post Trace(s) defined by `tpval` start.
+ */
+VALUE rb_tracepoint_enable(VALUE tpval);
+
+/**
+ * Stops (disables) an already running instance of TracePoint.
+ *
+ * @param[in] tpval An instance of TracePoint.
+ * @return Undefined value. Forget this. It should have returned `void`.
+ * @post Trace(s) defined by `tpval` stop.
+ */
+VALUE rb_tracepoint_disable(VALUE tpval);
+
+/**
+ * Queries if the passed TracePoint is up and running.
+ *
+ * @param[in] tpval An instance of TracePoint.
+ * @retval RUBY_Qtrue It is.
+ * @retval RUBY_Qfalse It isn't.
+ */
+VALUE rb_tracepoint_enabled_p(VALUE tpval);
+
+/**
+ * Type that represents a specific trace event. Roughly resembles the
+ * tracepoint object that is passed to the block of `TracePoint.new`:
+ *
+ * ```ruby
+ * TracePoint.new(*events) do |obj|
+ * ... # ^^^^^ Resembles this object.
+ * end
+ * ```
+ */
+typedef struct rb_trace_arg_struct rb_trace_arg_t;
+
+RBIMPL_ATTR_RETURNS_NONNULL()
+/**
+ * Queries the current event of the passed tracepoint.
+ *
+ * @param[in] tpval An instance of TracePoint.
+ * @exception rb_eRuntimeError `tpval` is disabled.
+ * @return The current event.
+ *
+ * @internal
+ *
+ * `tpval` is a fake. There is only one instance of ::rb_trace_arg_t at one
+ * time. This function just returns that global variable.
+ */
+rb_trace_arg_t *rb_tracearg_from_tracepoint(VALUE tpval);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the event of the passed trace.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @return Its event.
+ */
+rb_event_flag_t rb_tracearg_event_flag(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_tracearg_event_flag(), except it returns the name of the
+ * event in Ruby's symbol.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @return Its event, in Ruby level Symbol object.
+ */
+VALUE rb_tracearg_event(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the line of the point where the trace is at.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @retval 0 The trace is not at Ruby frame.
+ * @return otherwise Its line number.
+ */
+VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the file name of the point where the trace is at.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @retval RUBY_Qnil The trace is not at Ruby frame.
+ * @retval otherwise Its path.
+ */
+VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ *
+ * Queries the parameters passed on a call or return event.
+ *
+ * @param[in] trace_arg A trace instance
+ * @exception rb_eRuntimeError The tracing event does not support querying parameters.
+ * @return Array of parameters in the format of `Method#parameters`.
+ *
+ */
+VALUE rb_tracearg_parameters(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the method name of the point where the trace is at.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @retval RUBY_Qnil There is no method.
+ * @retval otherwise Its method name, in Ruby level Symbol.
+ */
+VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_tracearg_method_id(), except it returns callee id like
+ * rb_frame_callee().
+ *
+ * @param[in] trace_arg A trace instance.
+ * @retval RUBY_Qnil There is no method.
+ * @retval otherwise Its method name, in Ruby level Symbol.
+ */
+VALUE rb_tracearg_callee_id(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the class that defines the method that the passed trace is at. This
+ * can be different from the class of rb_tracearg_self()'s return value because
+ * of inheritance(s).
+ *
+ * @param[in] trace_arg A trace instance.
+ * @retval RUBY_Qnil There is no method.
+ * @retval otherwise Its method's class.
+ */
+VALUE rb_tracearg_defined_class(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Creates a binding object of the point where the trace is at.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @retval RUBY_Qnil The point has no binding.
+ * @retval otherwise Its binding.
+ *
+ * @internal
+ *
+ * @shyouhei has no idea on which situation shall this function return
+ * ::RUBY_Qnil.
+ */
+VALUE rb_tracearg_binding(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the receiver of the point trace is at.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @return Its receiver.
+ */
+VALUE rb_tracearg_self(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the return value that the trace represents.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @exception rb_eRuntimeError The tracing event is not return-related.
+ * @return The return value.
+ */
+VALUE rb_tracearg_return_value(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the raised exception that the trace represents.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @exception rb_eRuntimeError The tracing event is not exception-related.
+ * @return The raised exception.
+ */
+VALUE rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the compiled source code of the 'script_compiled' event.
+ * If loaded from a file, it will return nil.
+ *
+ * @param[in] trace_arg A trace instance
+ * @exception rb_eRuntimeError The tracing event is not 'script_compiled'.
+ * @retval RUBY_Qnil The script was loaded from a file.
+ * @retval otherwise The compiled source code.
+ *
+ */
+VALUE rb_tracearg_eval_script(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ *
+ * Queries the compiled instruction sequence on a 'script_compiled' event.
+ * Note that this method is MRI specific.
+ *
+ * @param[in] trace_arg A trace instance
+ * @exception rb_eRuntimeError The tracing event is not 'script_compiled'.
+ * @return The `RubyVM::InstructionSequence` object representing the instruction sequence.
+ *
+ */
+VALUE rb_tracearg_instruction_sequence(rb_trace_arg_t *trace_arg);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Queries the allocated/deallocated object that the trace represents.
+ *
+ * @param[in] trace_arg A trace instance.
+ * @exception rb_eRuntimeError The tracing event is not GC-related.
+ * @return The allocated/deallocated object.
+ */
+VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg);
+
+
+/** @} */
+
+/**
+ * @name Postponed Job API
+ *
+ * @{
+ */
+
+/*
+ * Postponed Job API
+ *
+ * This API is designed to be called from contexts where it is not safe to run Ruby
+ * code (e.g. because they do not hold the GVL or because GC is in progress), and
+ * defer a callback to run in a context where it _is_ safe. The primary intended
+ * users of this API is for sampling profilers like the "stackprof" gem; these work
+ * by scheduling the periodic delivery of a SIGPROF signal, and inside the C-level
+ * signal handler, deferring a job to collect a Ruby backtrace when it is next safe
+ * to do so.
+ *
+ * Ruby maintains a small, fixed-size postponed job table. An extension using this
+ * API should first call `rb_postponed_job_preregister` to register a callback
+ * function in this table and obtain a handle of type `rb_postponed_job_handle_t`
+ * to it. Subsequently, the callback can be triggered by calling
+ * `rb_postponed_job_trigger` with that handle, or the `data` associated with the
+ * callback function can be changed by calling `rb_postponed_job_preregister` again.
+ *
+ * Because the postponed job table is quite small (it only has 32 entries on most
+ * common systems), extensions should generally only preregister one or two `func`
+ * values.
+ *
+ * Historically, this API provided two functions `rb_postponed_job_register` and
+ * `rb_postponed_job_register_one`, which claimed to be fully async-signal-safe and
+ * would call back the provided `func` and `data` at an appropriate time. However,
+ * these functions were subject to race conditions which could cause crashes when
+ * racing with Ruby's internal use of them. These two functions are still present,
+ * but are marked as deprecated and have slightly changed semantics:
+ *
+ * * rb_postponed_job_register now works like rb_postponed_job_register_one i.e.
+ * `func` will only be executed at most one time each time Ruby checks for
+ * interrupts, no matter how many times it is registered
+ * * They are also called with the last `data` to be registered, not the first
+ * (which is how rb_postponed_job_register_one previously worked)
+ */
+
+
+/**
+ * Type of postponed jobs.
+ *
+ * @param[in,out] arg What was passed to `rb_postponed_job_preregister`
+ */
+typedef void (*rb_postponed_job_func_t)(void *arg);
+
+/**
+ * The type of a handle returned from `rb_postponed_job_preregister` and
+ * passed to `rb_postponed_job_trigger`
+ */
+typedef unsigned int rb_postponed_job_handle_t;
+#define POSTPONED_JOB_HANDLE_INVALID ((rb_postponed_job_handle_t)UINT_MAX)
+
+/**
+ * Pre-registers a func in Ruby's postponed job preregistration table,
+ * returning an opaque handle which can be used to trigger the job later. Generally,
+ * this function will be called during the initialization routine of an extension.
+ *
+ * The returned handle can be used later to call `rb_postponed_job_trigger`. This will
+ * cause Ruby to call back into the registered `func` with `data` at a later time, in
+ * a context where the GVL is held and it is safe to perform Ruby allocations.
+ *
+ * If the given `func` was already pre-registered, this function will overwrite the
+ * stored data with the newly passed data, and return the same handle instance as
+ * was previously returned.
+ *
+ * If this function is called concurrently with the same `func`, then the stored data
+ * could be the value from either call (but will definitely be one of them).
+ *
+ * If this function is called to update the data concurrently with a call to
+ * `rb_postponed_job_trigger` on the same handle, it's undefined whether `func` will
+ * be called with the old data or the new data.
+ *
+ * Although the current implementation of this function is in fact async-signal-safe and
+ * has defined semantics when called concurrently on the same `func`, a future Ruby
+ * version might require that this method be called under the GVL; thus, programs which
+ * aim to be forward-compatible should call this method whilst holding the GVL.
+ *
+ * @param[in] flags Unused and ignored
+ * @param[in] func The function to be pre-registered
+ * @param[in] data The data to be pre-registered
+ * @retval POSTPONED_JOB_HANDLE_INVALID The job table is full; this registration
+ * did not succeed and no further registration will do so for
+ * the lifetime of the program.
+ * @retval otherwise A handle which can be passed to `rb_postponed_job_trigger`
+ */
+rb_postponed_job_handle_t rb_postponed_job_preregister(unsigned int flags, rb_postponed_job_func_t func, void *data);
+
+/**
+ * Triggers a pre-registered job registered with rb_postponed_job_preregister,
+ * scheduling it for execution the next time the Ruby VM checks for interrupts.
+ * The context in which the job is called in holds the GVL and is safe to perform
+ * Ruby allocations within (i.e. it is not during GC).
+ *
+ * This method is async-signal-safe and can be called from any thread, at any
+ * time, including in signal handlers.
+ *
+ * If this method is called multiple times, Ruby will coalesce this into only
+ * one call to the job the next time it checks for interrupts.
+ *
+ * @params[in] h A handle returned from rb_postponed_job_preregister
+ */
+void rb_postponed_job_trigger(rb_postponed_job_handle_t h);
+
+/**
+ * Schedules the given `func` to be called with `data` when Ruby next checks for
+ * interrupts. If this function is called multiple times in between Ruby checking
+ * for interrupts, then `func` will be called only once with the `data` value from
+ * the first call to this function.
+ *
+ * Like `rb_postponed_job_trigger`, the context in which the job is called
+ * holds the GVL and can allocate Ruby objects.
+ *
+ * This method essentially has the same semantics as:
+ *
+ * ```
+ * rb_postponed_job_trigger(rb_postponed_job_preregister(func, data));
+ * ```
+ *
+ * @note Previous versions of Ruby promised that the (`func`, `data`) pairs would
+ * be executed as many times as they were registered with this function; in
+ * reality this was always subject to race conditions and this function no
+ * longer provides this guarantee. Instead, multiple calls to this function
+ * can be coalesced into a single execution of the passed `func`, with the
+ * most recent `data` registered at that time passed in.
+ *
+ * @deprecated This interface implies that arbitrarily many `func`'s can be enqueued
+ * over the lifetime of the program, whilst in reality the registration
+ * slots for postponed jobs are a finite resource. This is made clearer
+ * by the `rb_postponed_job_preregister` and `rb_postponed_job_trigger`
+ * functions, and a future version of Ruby might delete this function.
+ *
+ * @param[in] flags Unused and ignored.
+ * @param[in] func Job body.
+ * @param[in,out] data Passed as-is to `func`.
+ * @retval 0 Postponed job registration table is full. Failed.
+ * @retval 1 Registration succeeded.
+ * @post The passed job will run on the next interrupt check.
+ */
+ RBIMPL_ATTR_DEPRECATED(("use rb_postponed_job_preregister and rb_postponed_job_trigger"))
+int rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data);
+
+/**
+ * Identical to `rb_postponed_job_register`
+ *
+ * @deprecated This is deprecated for the same reason as `rb_postponed_job_register`
+ *
+ * @param[in] flags Unused and ignored.
+ * @param[in] func Job body.
+ * @param[in,out] data Passed as-is to `func`.
+ * @retval 0 Postponed job registration table is full. Failed.
+ * @retval 1 Registration succeeded.
+ * @post The passed job will run on the next interrupt check.
+ */
+ RBIMPL_ATTR_DEPRECATED(("use rb_postponed_job_preregister and rb_postponed_job_trigger"))
+int rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data);
+
+/** @} */
+
+/**
+ * @cond INTERNAL_MACRO
+ *
+ * Anything after this are intentionally left undocumented, to honour the
+ * comment below.
+ */
+
+/* undocumented advanced tracing APIs */
+
+typedef enum {
+ RUBY_EVENT_HOOK_FLAG_SAFE = 0x01,
+ RUBY_EVENT_HOOK_FLAG_DELETED = 0x02,
+ RUBY_EVENT_HOOK_FLAG_RAW_ARG = 0x04
+} rb_event_hook_flag_t;
+
+void rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flag);
+void rb_thread_add_event_hook2(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flag);
+
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_DEBUG_H */
diff --git a/include/ruby/defines.h b/include/ruby/defines.h
new file mode 100644
index 0000000000..48184f8a18
--- /dev/null
+++ b/include/ruby/defines.h
@@ -0,0 +1,116 @@
+#ifndef RUBY_DEFINES_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_DEFINES_H 1
+/**
+ * @file
+ * @author $Author$
+ * @date Wed May 18 00:21:44 JST 1994
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+
+#include "ruby/internal/config.h"
+
+/* AC_INCLUDES_DEFAULT */
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#ifdef HAVE_STDALIGN_H
+# include <stdalign.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+#ifdef RUBY_USE_SETJMPEX
+# include <setjmpex.h>
+#endif
+
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/xmalloc.h"
+#include "ruby/backward/2/assume.h"
+#include "ruby/backward/2/attributes.h"
+#include "ruby/backward/2/bool.h"
+#include "ruby/backward/2/long_long.h"
+#include "ruby/backward/2/stdalign.h"
+#include "ruby/backward/2/stdarg.h"
+#include "ruby/internal/dosish.h"
+#include "ruby/missing.h"
+
+/**
+ * Asserts that the compilation unit includes Ruby's CAPI. This has been here
+ * since the very beginning (at least since version 0.49).
+ */
+#define RUBY
+
+#ifdef __GNUC__
+# /** This is expanded to nothing for non-GCC compilers. */
+# define RB_GNUC_EXTENSION __extension__
+# /** This is expanded to the passed token for non-GCC compilers. */
+# define RB_GNUC_EXTENSION_BLOCK(x) __extension__ ({ x; })
+#else
+# define RB_GNUC_EXTENSION
+# define RB_GNUC_EXTENSION_BLOCK(x) (x)
+#endif
+
+/** @cond INTERNAL_MACRO */
+
+/* :FIXME: Can someone tell us why is this macro defined here? @shyouhei
+ * thinks this is a truly internal macro but cannot move around because he
+ * doesn't understand the reason of this arrangement. */
+#ifndef RUBY_MBCHAR_MAXSIZE
+# define RUBY_MBCHAR_MAXSIZE INT_MAX
+# /* MB_CUR_MAX will not work well in C locale */
+#endif
+
+#if defined(__sparc)
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+void rb_sparc_flush_register_windows(void);
+RBIMPL_SYMBOL_EXPORT_END()
+# define FLUSH_REGISTER_WINDOWS rb_sparc_flush_register_windows()
+#else
+# define FLUSH_REGISTER_WINDOWS ((void)0)
+#endif
+/** @endcond */
+#endif /* RUBY_DEFINES_H */
diff --git a/include/ruby/encoding.h b/include/ruby/encoding.h
new file mode 100644
index 0000000000..1256393701
--- /dev/null
+++ b/include/ruby/encoding.h
@@ -0,0 +1,31 @@
+#ifndef RUBY_ENCODING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_ENCODING_H 1
+/**
+ * @file
+ * @author $Author: matz $
+ * @date Thu May 24 11:49:41 JST 2007
+ * @copyright Copyright (C) 2007 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @brief Encoding relates APIs.
+ *
+ * These APIs are mainly for implementing encodings themselves. Encodings are
+ * built on top of Ruby's core CAPIs. Though not prohibited, there can be
+ * relatively less rooms for things in this header file be useful when writing
+ * an extension library.
+ */
+#include "ruby/ruby.h"
+
+#include "ruby/internal/encoding/coderange.h"
+#include "ruby/internal/encoding/ctype.h"
+#include "ruby/internal/encoding/encoding.h"
+#include "ruby/internal/encoding/pathname.h"
+#include "ruby/internal/encoding/re.h"
+#include "ruby/internal/encoding/sprintf.h"
+#include "ruby/internal/encoding/string.h"
+#include "ruby/internal/encoding/symbol.h"
+#include "ruby/internal/encoding/transcode.h"
+
+#endif /* RUBY_ENCODING_H */
diff --git a/include/ruby/fiber/scheduler.h b/include/ruby/fiber/scheduler.h
new file mode 100644
index 0000000000..4d764f68ae
--- /dev/null
+++ b/include/ruby/fiber/scheduler.h
@@ -0,0 +1,505 @@
+#ifndef RUBY_FIBER_SCHEDULER_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_FIBER_SCHEDULER_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @brief Scheduler APIs.
+ */
+#include "ruby/internal/config.h"
+
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+#include <stddef.h> /* size_t */
+#endif
+
+#include "ruby/ruby.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/arithmetic.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+// Version 3: Adds support for `fiber_interrupt`.
+#define RUBY_FIBER_SCHEDULER_VERSION 3
+
+struct timeval;
+struct rb_thread_struct;
+
+/**
+ * Wrap a `ssize_t` and `int errno` into a single `VALUE`. This interface should
+ * be used to safely capture results from system calls like `read` and `write`.
+ *
+ * You should use `rb_fiber_scheduler_io_result_apply` to unpack the result of
+ * this value and update `int errno`.
+ *
+ * You should not directly try to interpret the result value as it is considered
+ * an opaque representation. However, the general representation is an integer
+ * in the range of `[-int errno, size_t size]`. Linux generally restricts the
+ * result of system calls like `read` and `write` to `<= 2^31` which means this
+ * will typically fit within a single FIXNUM.
+ *
+ * @param[in] result The result of the system call.
+ * @param[in] error The value of `errno`.
+ * @return A `VALUE` which contains the result and/or errno.
+ */
+static inline VALUE
+rb_fiber_scheduler_io_result(ssize_t result, int error)
+{
+ if (result == -1) {
+ return RB_INT2NUM(-error);
+ }
+ else {
+ return RB_SIZE2NUM(result);
+ }
+}
+
+/**
+ * Apply an io result to the local thread, returning the value of the original
+ * system call that created it and updating `int errno`.
+ *
+ * You should not directly try to interpret the result value as it is considered
+ * an opaque representation.
+ *
+ * @param[in] result The `VALUE` which contains an errno and/or result size.
+ * @post Updates `int errno` with the value if negative.
+ * @return The original result of the system call.
+ */
+static inline ssize_t
+rb_fiber_scheduler_io_result_apply(VALUE result)
+{
+ if (RB_FIXNUM_P(result) && RB_NUM2INT(result) < 0) {
+ errno = -RB_NUM2INT(result);
+ return -1;
+ }
+ else {
+ return RB_NUM2SIZE(result);
+ }
+}
+
+/**
+ * Queries the current scheduler of the current thread that is calling this
+ * function.
+ *
+ * @retval RUBY_Qnil No scheduler has been set so far to this thread (which
+ * is the default).
+ * @retval otherwise The scheduler that was last set for the current thread
+ * with rb_fiber_scheduler_set().
+ */
+VALUE rb_fiber_scheduler_get(void);
+
+/**
+ * Destructively assigns the passed scheduler to that of the current thread
+ * that is calling this function. If the scheduler is set, non-blocking fibers
+ * (created by `Fiber.new` with `blocking: false`, or by `Fiber.schedule`) call
+ * that scheduler's hook methods on potentially blocking operations, and the
+ * current thread will call scheduler's `#close` method on finalisation
+ * (allowing the scheduler to properly manage all non-finished fibers).
+ * `scheduler` can be an object of any class corresponding to
+ * `Fiber::Scheduler` interface. Its implementation is up to the user.
+ *
+ * @param[in] scheduler The scheduler to set.
+ * @exception rb_eArgError `scheduler` does not conform the interface.
+ * @post Current thread's scheduler is `scheduler`.
+ */
+VALUE rb_fiber_scheduler_set(VALUE scheduler);
+
+/**
+ * Identical to rb_fiber_scheduler_get(), except it also returns ::RUBY_Qnil in
+ * case of a blocking fiber. As blocking fibers do not participate schedulers'
+ * scheduling this function can be handy.
+ *
+ * @retval RUBY_Qnil No scheduler is in effect.
+ * @retval otherwise The scheduler that is in effect, if any.
+ */
+VALUE rb_fiber_scheduler_current(void);
+
+/**
+ * Identical to rb_fiber_scheduler_current(), except it queries for that of the
+ * passed thread value instead of the implicit current one.
+ *
+ * @param[in] thread Target thread.
+ * @exception rb_eTypeError `thread` is not a thread.
+ * @retval RUBY_Qnil No scheduler is in effect in `thread`.
+ * @retval otherwise The scheduler that is in effect in `thread`.
+ */
+VALUE rb_fiber_scheduler_current_for_thread(VALUE thread);
+
+/**
+ * Identical to rb_fiber_scheduler_current_for_thread(), except it expects
+ * a threadptr instead of a thread value.
+ *
+ * @param[in] thread Target thread.
+ * @exception rb_eTypeError `thread` is not a thread.
+ * @retval RUBY_Qnil No scheduler is in effect in `thread`.
+ * @retval otherwise The scheduler that is in effect in `thread`.
+ */
+VALUE rb_fiber_scheduler_current_for_threadptr(struct rb_thread_struct *thread);
+
+/**
+ * Converts the passed timeout to an expression that rb_fiber_scheduler_block()
+ * etc. expects.
+ *
+ * @param[in] timeout A duration (can be `NULL`).
+ * @retval RUBY_Qnil No timeout (blocks indefinitely).
+ * @retval otherwise A timeout object.
+ */
+VALUE rb_fiber_scheduler_make_timeout(struct timeval *timeout);
+
+/**
+ * Closes the passed scheduler object. This expects the scheduler to wait for
+ * all fibers. Thus the scheduler's main loop tends to start here.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @return What `scheduler.close` returns.
+ */
+VALUE rb_fiber_scheduler_close(VALUE scheduler);
+
+/**
+ * Non-blocking `sleep`. Depending on scheduler implementation, this for
+ * instance switches to another fiber etc.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] duration Passed as-is to `scheduler.kernel_sleep`.
+ * @return What `scheduler.kernel_sleep` returns.
+ */
+VALUE rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE duration);
+
+/**
+ * Identical to rb_fiber_scheduler_kernel_sleep(), except it can pass multiple
+ * arguments.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed as-is to `scheduler.kernel_sleep`
+ * @return What `scheduler.kernel_sleep` returns.
+ */
+VALUE rb_fiber_scheduler_kernel_sleepv(VALUE scheduler, int argc, VALUE * argv);
+
+/**
+ * Yield to the scheduler, to be resumed on the next scheduling cycle.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @return What `scheduler.yield` returns.
+ */
+VALUE rb_fiber_scheduler_yield(VALUE scheduler);
+
+/* Description TBW */
+#if 0
+VALUE rb_fiber_scheduler_timeout_after(VALUE scheduler, VALUE timeout, VALUE exception, VALUE message);
+VALUE rb_fiber_scheduler_timeout_afterv(VALUE scheduler, int argc, VALUE * argv);
+int rb_fiber_scheduler_supports_process_wait(VALUE scheduler);
+#endif
+
+/**
+ * Non-blocking `waitpid`. Depending on scheduler implementation, this for
+ * instance switches to another fiber etc.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] pid Process ID to wait.
+ * @param[in] flags Wait flags, e.g. `WUNTRACED`.
+ * @return What `scheduler.process_wait` returns.
+ */
+VALUE rb_fiber_scheduler_process_wait(VALUE scheduler, rb_pid_t pid, int flags);
+
+/**
+ * Non-blocking wait for the passed "blocker", which is for instance
+ * `Thread.join` or `Mutex.lock`. Depending on scheduler implementation, this
+ * for instance switches to another fiber etc.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] blocker What blocks the current fiber.
+ * @param[in] timeout Numeric timeout.
+ * @return What `scheduler.block` returns.
+ */
+VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout);
+
+/**
+ * Wakes up a fiber previously blocked using rb_fiber_scheduler_block().
+ *
+ * This function may be called from a different thread.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] blocker What was awaited for.
+ * @param[in] fiber What to unblock.
+ * @return What `scheduler.unblock` returns.
+ */
+VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber);
+
+/**
+ * Non-blocking version of rb_io_wait(). Depending on scheduler
+ * implementation, this for instance switches to another fiber etc.
+ *
+ * The "events" here is a Ruby level integer, which is an OR-ed value of
+ * `IO::READABLE`, `IO::WRITABLE`, and `IO::PRIORITY`.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to wait.
+ * @param[in] events An integer set of interests.
+ * @param[in] timeout Numeric timeout.
+ * @return What `scheduler.io_wait` returns.
+ */
+VALUE rb_fiber_scheduler_io_wait(VALUE scheduler, VALUE io, VALUE events, VALUE timeout);
+
+/**
+ * Non-blocking wait until the passed IO is ready for reading. This is a
+ * special case of rb_fiber_scheduler_io_wait(), where the interest is
+ * `IO::READABLE` and timeout is never.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to wait.
+ * @return What `scheduler.io_wait` returns.
+ */
+VALUE rb_fiber_scheduler_io_wait_readable(VALUE scheduler, VALUE io);
+
+/**
+ * Non-blocking wait until the passed IO is ready for writing. This is a
+ * special case of rb_fiber_scheduler_io_wait(), where the interest is
+ * `IO::WRITABLE` and timeout is never.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to wait.
+ * @return What `scheduler.io_wait` returns.
+ */
+VALUE rb_fiber_scheduler_io_wait_writable(VALUE scheduler, VALUE io);
+
+/**
+ * Non-blocking version of `IO.select`.
+ *
+ * It's possible that this will be emulated using a thread, so you should not
+ * rely on it for high performance.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] readables An array of readable objects.
+ * @param[in] writables An array of writable objects.
+ * @param[in] exceptables An array of objects that might encounter exceptional conditions.
+ * @param[in] timeout Numeric timeout or nil.
+ * @return What `scheduler.io_select` returns, normally a 3-tuple of arrays of ready objects.
+ */
+VALUE rb_fiber_scheduler_io_select(VALUE scheduler, VALUE readables, VALUE writables, VALUE exceptables, VALUE timeout);
+
+/**
+ * Non-blocking version of `IO.select`, `argv` variant.
+ */
+VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv);
+
+/**
+ * Non-blocking read from the passed IO.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to read from.
+ * @param[in] buffer The buffer to read to.
+ * @param[in] length The minimum number of bytes to read.
+ * @param[in] offset The offset in the buffer to read from.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
+ * @return otherwise What `scheduler.io_read` returns `[-errno, size]`.
+ */
+VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset);
+
+/**
+ * Non-blocking write to the passed IO.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to write to.
+ * @param[in] buffer The buffer to write from.
+ * @param[in] length The minimum number of bytes to write.
+ * @param[in] offset The offset in the buffer to write from.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
+ * @return otherwise What `scheduler.io_write` returns `[-errno, size]`.
+ */
+VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset);
+
+/**
+ * Non-blocking read from the passed IO at the specified offset.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to read from.
+ * @param[in] from The offset to read from.
+ * @param[in] buffer The buffer to read to.
+ * @param[in] length The minimum number of bytes to read.
+ * @param[in] offset The offset in the buffer to read to.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
+ * @return otherwise What `scheduler.io_read` returns.
+ */
+VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset);
+
+/**
+ * Non-blocking write to the passed IO at the specified offset.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to write to.
+ * @param[in] from The offset to write to.
+ * @param[in] buffer The buffer to write from.
+ * @param[in] length The minimum number of bytes to write.
+ * @param[in] offset The offset in the buffer to write from.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
+ * @return otherwise What `scheduler.io_write` returns.
+ */
+VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset);
+
+/**
+ * Non-blocking read from the passed IO using a native buffer.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to read from.
+ * @param[in] base The memory to read to.
+ * @param[in] size Size of the memory.
+ * @param[in] length The minimum number of bytes to read.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
+ * @return otherwise What `scheduler.io_read` returns.
+ */
+VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *base, size_t size, size_t length);
+
+/**
+ * Non-blocking write to the passed IO using a native buffer.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to write to.
+ * @param[in] base The memory to write from.
+ * @param[in] size Size of the memory.
+ * @param[in] length The minimum number of bytes to write.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
+ * @return otherwise What `scheduler.io_write` returns.
+ */
+VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *base, size_t size, size_t length);
+
+/**
+ * Non-blocking pread from the passed IO using a native buffer.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to read from.
+ * @param[in] from The offset to read from.
+ * @param[in] base The memory to read to.
+ * @param[in] size Size of the memory.
+ * @param[in] length The minimum number of bytes to read.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
+ * @return otherwise What `scheduler.io_read` returns.
+ */
+VALUE rb_fiber_scheduler_io_pread_memory(VALUE scheduler, VALUE io, rb_off_t from, void *base, size_t size, size_t length);
+
+/**
+ * Non-blocking pwrite to the passed IO using a native buffer.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to write to.
+ * @param[in] from The offset to write from.
+ * @param[in] base The memory to write from.
+ * @param[in] size Size of the memory.
+ * @param[in] length The minimum number of bytes to write.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
+ * @return otherwise What `scheduler.io_write` returns.
+ */
+VALUE rb_fiber_scheduler_io_pwrite_memory(VALUE scheduler, VALUE io, rb_off_t from, const void *base, size_t size, size_t length);
+
+/**
+ * Non-blocking close the given IO.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] io An io object to close.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#io_close`.
+ * @return otherwise What `scheduler.io_close` returns.
+ */
+VALUE rb_fiber_scheduler_io_close(VALUE scheduler, VALUE io);
+
+/**
+ * Non-blocking DNS lookup.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] hostname A host name to query.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#address_resolve`.
+ * @return otherwise What `scheduler.address_resolve` returns.
+ */
+VALUE rb_fiber_scheduler_address_resolve(VALUE scheduler, VALUE hostname);
+
+// The state of the blocking operation execution.
+struct rb_fiber_scheduler_blocking_operation_state {
+ void *result;
+ int saved_errno;
+};
+
+// The opaque handle for the blocking operation.
+typedef struct rb_fiber_scheduler_blocking_operation rb_fiber_scheduler_blocking_operation_t;
+
+/**
+ * Extract the blocking operation handle from a BlockingOperationRuby object.
+ *
+ * This function safely extracts the opaque handle from a BlockingOperation VALUE
+ * while holding the GVL. The returned pointer can be passed to worker threads
+ * and used with rb_fiber_scheduler_blocking_operation_execute.
+ *
+ * @param[in] self The BlockingOperation VALUE to extract from
+ * @return The opaque struct pointer on success, NULL on error
+ * @note Experimental.
+ */
+rb_fiber_scheduler_blocking_operation_t *rb_fiber_scheduler_blocking_operation_extract(VALUE self);
+
+/**
+ * Execute blocking operation from handle (GVL not required).
+ *
+ * This function executes a blocking operation using the opaque handle
+ * obtained from rb_fiber_scheduler_blocking_operation_extract.
+ * It can be called from native threads without holding the GVL.
+ *
+ * @param[in] blocking_operation The opaque handle.
+ * @return 0 on success, -1 on error.
+ * @note Experimental. Can be called from any thread without holding the GVL
+ */
+int rb_fiber_scheduler_blocking_operation_execute(rb_fiber_scheduler_blocking_operation_t *blocking_operation);
+
+/**
+ * Cancel a blocking operation.
+ *
+ * This function cancels a blocking operation. If the operation is queued,
+ * it just marks it as cancelled. If it's executing, it marks it as cancelled
+ * and calls the unblock function to interrupt the operation.
+ *
+ * @param blocking_operation The opaque struct pointer
+ * @return 1 if unblock function was called, 0 if just marked cancelled, -1 on error
+ * @note Experimental.
+ */
+int rb_fiber_scheduler_blocking_operation_cancel(rb_fiber_scheduler_blocking_operation_t *blocking_operation);
+
+/**
+ * Defer the execution of the passed function to the scheduler.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] function The function to run.
+ * @param[in] data The data to pass to the function.
+ * @param[in] unblock_function The unblock function to use to interrupt the operation.
+ * @param[in] data2 The data to pass to the unblock function.
+ * @param[in] flags Flags passed to `rb_nogvl`.
+ * @param[out] state The result and errno of the operation.
+ * @retval RUBY_Qundef `scheduler` doesn't have `#blocking_operation_wait`.
+ * @return otherwise What `scheduler.blocking_operation_wait` returns.
+ */
+VALUE rb_fiber_scheduler_blocking_operation_wait(VALUE scheduler, void* (*function)(void *), void *data, rb_unblock_function_t *unblock_function, void *data2, int flags, struct rb_fiber_scheduler_blocking_operation_state *state);
+
+/**
+ * Interrupt a fiber by raising an exception. You can construct an exception using `rb_make_exception`.
+ *
+ * This hook may be invoked by a different thread.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] fiber The fiber to interrupt.
+ * @param[in] exception The exception to raise in the fiber.
+ * @return What `scheduler.fiber_interrupt` returns.
+ */
+VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exception);
+
+/**
+ * Create and schedule a non-blocking fiber.
+ *
+ * @param[in] scheduler Target scheduler.
+ * @param[in] argc Number of arguments in argv.
+ * @param[in] argv Array of arguments to pass to the fiber.
+ * @param[in] kw_splat Whether to expand last argument as keywords.
+ * @return The created and scheduled fiber.
+ */
+VALUE rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_FIBER_SCHEDULER_H */
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
new file mode 100644
index 0000000000..8718169ce2
--- /dev/null
+++ b/include/ruby/intern.h
@@ -0,0 +1,64 @@
+#ifndef RUBY_INTERN_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERN_H 1
+/**
+ * @file
+ * @author $Author$
+ * @date Thu Jun 10 14:22:17 JST 1993
+ * @copyright Copyright (C) 1993-2007 Yukihiro Matsumoto
+ * @copyright Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ * @copyright Copyright (C) 2000 Information-technology Promotion Agency, Japan
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/defines.h"
+
+#include <stdarg.h>
+
+#include "ruby/st.h"
+
+/*
+ * Functions and variables that are used by more than one source file of
+ * the kernel.
+ */
+
+#include "ruby/internal/intern/array.h"
+#include "ruby/internal/intern/bignum.h"
+#include "ruby/internal/intern/class.h"
+#include "ruby/internal/intern/compar.h"
+#include "ruby/internal/intern/complex.h"
+#include "ruby/internal/intern/cont.h"
+#include "ruby/internal/intern/dir.h"
+#include "ruby/internal/intern/enum.h"
+#include "ruby/internal/intern/enumerator.h"
+#include "ruby/internal/intern/error.h"
+#include "ruby/internal/intern/eval.h"
+#include "ruby/internal/intern/file.h"
+#include "ruby/internal/intern/hash.h"
+#include "ruby/internal/intern/io.h"
+#include "ruby/internal/intern/load.h"
+#include "ruby/internal/intern/marshal.h"
+#include "ruby/internal/intern/numeric.h"
+#include "ruby/internal/intern/object.h"
+#include "ruby/internal/intern/parse.h"
+#include "ruby/internal/intern/proc.h"
+#include "ruby/internal/intern/process.h"
+#include "ruby/internal/intern/random.h"
+#include "ruby/internal/intern/range.h"
+#include "ruby/internal/intern/rational.h"
+#include "ruby/internal/intern/re.h"
+#include "ruby/internal/intern/ruby.h"
+#include "ruby/internal/intern/select.h"
+#include "ruby/internal/intern/set.h"
+#include "ruby/internal/intern/signal.h"
+#include "ruby/internal/intern/sprintf.h"
+#include "ruby/internal/intern/string.h"
+#include "ruby/internal/intern/struct.h"
+#include "ruby/internal/intern/thread.h"
+#include "ruby/internal/intern/time.h"
+#include "ruby/internal/intern/variable.h"
+#include "ruby/internal/intern/vm.h"
+
+#endif /* RUBY_INTERN_H */
diff --git a/include/ruby/internal/abi.h b/include/ruby/internal/abi.h
new file mode 100644
index 0000000000..e6d1fa7e8f
--- /dev/null
+++ b/include/ruby/internal/abi.h
@@ -0,0 +1,58 @@
+#ifndef RUBY_ABI_H
+#define RUBY_ABI_H
+
+#ifdef RUBY_ABI_VERSION /* should match the definition in config.h */
+
+/* This number represents Ruby's ABI version.
+ *
+ * In development Ruby, it should be bumped every time an ABI incompatible
+ * change is introduced. This will force other developers to rebuild extension
+ * gems.
+ *
+ * The following cases are considered as ABI incompatible changes:
+ * - Changing any data structures.
+ * - Changing macros or inline functions causing a change in behavior.
+ * - Deprecating or removing function declarations.
+ *
+ * The following cases are NOT considered as ABI incompatible changes:
+ * - Any changes that does not involve the header files in the `include`
+ * directory.
+ * - Adding macros, inline functions, or function declarations.
+ * - Backwards compatible refactors.
+ * - Editing comments.
+ *
+ * In released versions of Ruby, this number is not defined since teeny
+ * versions of Ruby should guarantee ABI compatibility.
+ */
+#define RUBY_ABI_VERSION 1
+
+/* Windows does not support weak symbols so ruby_abi_version will not exist
+ * in the shared library. */
+#if defined(HAVE_FUNC_WEAK) && !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
+# define RUBY_DLN_CHECK_ABI
+#endif
+#endif /* RUBY_ABI_VERSION */
+
+#if defined(RUBY_DLN_CHECK_ABI) && !defined(RUBY_EXPORT)
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+RUBY_FUNC_EXPORTED unsigned long long __attribute__((weak))
+ruby_abi_version(void)
+{
+# ifdef RUBY_ABI_VERSION
+ return RUBY_ABI_VERSION;
+# else
+ return 0;
+# endif
+}
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
+
+#endif
diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h
new file mode 100644
index 0000000000..e4c6d155cc
--- /dev/null
+++ b/include/ruby/internal/anyargs.h
@@ -0,0 +1,398 @@
+#ifndef RBIMPL_ANYARGS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ANYARGS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Function overloads to issue warnings around #ANYARGS.
+ *
+ * For instance ::rb_define_method takes a pointer to #ANYARGS -ed functions,
+ * which in fact varies 18 different prototypes. We still need to preserve
+ * #ANYARGS for storages but why not check the consistencies if possible. With
+ * those complex macro overlays defined in this header file, use of a function
+ * pointer gets checked against the corresponding arity argument.
+ *
+ * ### Q&A ###
+ *
+ * - Q: Where did the magic number "18" came from in the description above?
+ *
+ * - A: Count the case branch of `vm_method.c:call_cfunc_invoker_func()`. Note
+ * also that the 18 branches has lasted for at least 25 years. See also
+ * commit 200e0ee2fd3c1c006c528874a88f684447215524.
+ *
+ * - Q: What is this `__weakref__` thing?
+ *
+ * - A: That is a kind of function overloading mechanism that GCC provides. In
+ * this case for instance `rb_define_method_00` is an alias of
+ * ::rb_define_method, with a strong type.
+ *
+ * - Q: What is this `__transparent_union__` thing?
+ *
+ * A: That is another kind of function overloading mechanism that GCC
+ * provides. In this case the attributed function pointer is either
+ * `VALUE(*)(int,VALUE*,VALUE)` or `VALUE(*)(int,const VALUE*,VALUE)`.
+ *
+ * This is better than `void*` or #ANYARGS because we can reject all other
+ * possibilities than the two.
+ *
+ * - Q: What does this #rb_define_method macro mean?
+ *
+ * - A: It selects appropriate alias of the ::rb_define_method function,
+ * depending on the last (arity) argument.
+ *
+ * - Q: Why the special case for ::rb_f_notimplement ?
+ *
+ * - A: Function pointer to ::rb_f_notimplement is special cased in
+ * `vm_method.c:rb_add_method_cfunc()`. That should be handled by the
+ * `__builtin_choose_expr` chain inside of #rb_define_method macro
+ * expansion. In order to do so, comparison like
+ * `(func == rb_f_notimplement)` is inappropriate for
+ * `__builtin_choose_expr`'s expression (which must be a compile-time
+ * integer constant but the address of ::rb_f_notimplement is not fixed
+ * until the linker). Instead we are using
+ * `__builtin_types_compatible_p`, and in doing so we need to distinguish
+ * ::rb_f_notimplement from others, by type.
+ */
+#include "ruby/internal/attr/maybe_unused.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/weakref.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/config.h"
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/intern/class.h"
+#include "ruby/internal/intern/vm.h"
+#include "ruby/internal/method.h"
+#include "ruby/internal/value.h"
+#include "ruby/backward/2/stdarg.h"
+
+#if defined(__cplusplus)
+# include "ruby/backward/cxxanyargs.hpp"
+
+#elif defined(_WIN32) || defined(__CYGWIN__)
+# /* Skip due to [Bug #16134] */
+# define RBIMPL_CAST_FN_PTR 1
+
+#elif ! RBIMPL_HAS_ATTRIBUTE(transparent_union)
+# /* :TODO: improve here, please find a way to support. */
+# define RBIMPL_CAST_FN_PTR 1
+
+#elif ! defined(HAVE_VA_ARGS_MACRO)
+# /* :TODO: improve here, please find a way to support. */
+# define RBIMPL_CAST_FN_PTR 1
+
+#else
+# /** @cond INTERNAL_MACRO */
+# if ! defined(HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P)
+# define RBIMPL_CFUNC_IS_rb_f_notimplement(f) 0
+# else
+# define RBIMPL_CFUNC_IS_rb_f_notimplement(f) \
+ __builtin_types_compatible_p( \
+ __typeof__(f), \
+ __typeof__(rb_f_notimplement))
+# endif
+
+# if ! defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
+# define RBIMPL_ANYARGS_DISPATCH(expr, truthy, falsy) (falsy)
+# else
+# define RBIMPL_ANYARGS_DISPATCH(expr, truthy, falsy) \
+ __builtin_choose_expr( \
+ __builtin_choose_expr( \
+ __builtin_constant_p(expr), \
+ (expr), 0), \
+ (truthy), (falsy))
+# endif
+
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_m2(n) RBIMPL_ANYARGS_DISPATCH((n) == -2, rb_define_singleton_method_m2, rb_define_singleton_method_m3)
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_m1(n) RBIMPL_ANYARGS_DISPATCH((n) == -1, rb_define_singleton_method_m1, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_m2(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_00(n) RBIMPL_ANYARGS_DISPATCH((n) == 0, rb_define_singleton_method_00, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_m1(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_01(n) RBIMPL_ANYARGS_DISPATCH((n) == 1, rb_define_singleton_method_01, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_00(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_02(n) RBIMPL_ANYARGS_DISPATCH((n) == 2, rb_define_singleton_method_02, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_01(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_03(n) RBIMPL_ANYARGS_DISPATCH((n) == 3, rb_define_singleton_method_03, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_02(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_04(n) RBIMPL_ANYARGS_DISPATCH((n) == 4, rb_define_singleton_method_04, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_03(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_05(n) RBIMPL_ANYARGS_DISPATCH((n) == 5, rb_define_singleton_method_05, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_04(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_06(n) RBIMPL_ANYARGS_DISPATCH((n) == 6, rb_define_singleton_method_06, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_05(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_07(n) RBIMPL_ANYARGS_DISPATCH((n) == 7, rb_define_singleton_method_07, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_06(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_08(n) RBIMPL_ANYARGS_DISPATCH((n) == 8, rb_define_singleton_method_08, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_07(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_09(n) RBIMPL_ANYARGS_DISPATCH((n) == 9, rb_define_singleton_method_09, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_08(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_10(n) RBIMPL_ANYARGS_DISPATCH((n) == 10, rb_define_singleton_method_10, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_09(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_11(n) RBIMPL_ANYARGS_DISPATCH((n) == 11, rb_define_singleton_method_11, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_10(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_12(n) RBIMPL_ANYARGS_DISPATCH((n) == 12, rb_define_singleton_method_12, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_11(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_singleton_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_12(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_singleton_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_13(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_singleton_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_14(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_m2(n) RBIMPL_ANYARGS_DISPATCH((n) == -2, rb_define_protected_method_m2, rb_define_protected_method_m3)
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_m1(n) RBIMPL_ANYARGS_DISPATCH((n) == -1, rb_define_protected_method_m1, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_m2(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_00(n) RBIMPL_ANYARGS_DISPATCH((n) == 0, rb_define_protected_method_00, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_m1(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_01(n) RBIMPL_ANYARGS_DISPATCH((n) == 1, rb_define_protected_method_01, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_00(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_02(n) RBIMPL_ANYARGS_DISPATCH((n) == 2, rb_define_protected_method_02, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_01(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_03(n) RBIMPL_ANYARGS_DISPATCH((n) == 3, rb_define_protected_method_03, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_02(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_04(n) RBIMPL_ANYARGS_DISPATCH((n) == 4, rb_define_protected_method_04, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_03(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_05(n) RBIMPL_ANYARGS_DISPATCH((n) == 5, rb_define_protected_method_05, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_04(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_06(n) RBIMPL_ANYARGS_DISPATCH((n) == 6, rb_define_protected_method_06, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_05(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_07(n) RBIMPL_ANYARGS_DISPATCH((n) == 7, rb_define_protected_method_07, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_06(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_08(n) RBIMPL_ANYARGS_DISPATCH((n) == 8, rb_define_protected_method_08, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_07(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_09(n) RBIMPL_ANYARGS_DISPATCH((n) == 9, rb_define_protected_method_09, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_08(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_10(n) RBIMPL_ANYARGS_DISPATCH((n) == 10, rb_define_protected_method_10, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_09(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_11(n) RBIMPL_ANYARGS_DISPATCH((n) == 11, rb_define_protected_method_11, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_10(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_12(n) RBIMPL_ANYARGS_DISPATCH((n) == 12, rb_define_protected_method_12, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_11(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_protected_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_12(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_protected_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_13(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_protected_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_14(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_m2(n) RBIMPL_ANYARGS_DISPATCH((n) == -2, rb_define_private_method_m2, rb_define_private_method_m3)
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_m1(n) RBIMPL_ANYARGS_DISPATCH((n) == -1, rb_define_private_method_m1, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_m2(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_00(n) RBIMPL_ANYARGS_DISPATCH((n) == 0, rb_define_private_method_00, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_m1(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_01(n) RBIMPL_ANYARGS_DISPATCH((n) == 1, rb_define_private_method_01, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_00(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_02(n) RBIMPL_ANYARGS_DISPATCH((n) == 2, rb_define_private_method_02, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_01(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_03(n) RBIMPL_ANYARGS_DISPATCH((n) == 3, rb_define_private_method_03, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_02(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_04(n) RBIMPL_ANYARGS_DISPATCH((n) == 4, rb_define_private_method_04, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_03(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_05(n) RBIMPL_ANYARGS_DISPATCH((n) == 5, rb_define_private_method_05, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_04(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_06(n) RBIMPL_ANYARGS_DISPATCH((n) == 6, rb_define_private_method_06, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_05(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_07(n) RBIMPL_ANYARGS_DISPATCH((n) == 7, rb_define_private_method_07, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_06(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_08(n) RBIMPL_ANYARGS_DISPATCH((n) == 8, rb_define_private_method_08, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_07(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_09(n) RBIMPL_ANYARGS_DISPATCH((n) == 9, rb_define_private_method_09, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_08(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_10(n) RBIMPL_ANYARGS_DISPATCH((n) == 10, rb_define_private_method_10, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_09(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_11(n) RBIMPL_ANYARGS_DISPATCH((n) == 11, rb_define_private_method_11, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_10(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_12(n) RBIMPL_ANYARGS_DISPATCH((n) == 12, rb_define_private_method_12, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_11(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_private_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_12(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_private_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_13(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_private_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_14(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_m2(n) RBIMPL_ANYARGS_DISPATCH((n) == -2, rb_define_module_function_m2, rb_define_module_function_m3)
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_m1(n) RBIMPL_ANYARGS_DISPATCH((n) == -1, rb_define_module_function_m1, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_m2(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_00(n) RBIMPL_ANYARGS_DISPATCH((n) == 0, rb_define_module_function_00, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_m1(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_01(n) RBIMPL_ANYARGS_DISPATCH((n) == 1, rb_define_module_function_01, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_00(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_02(n) RBIMPL_ANYARGS_DISPATCH((n) == 2, rb_define_module_function_02, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_01(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_03(n) RBIMPL_ANYARGS_DISPATCH((n) == 3, rb_define_module_function_03, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_02(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_04(n) RBIMPL_ANYARGS_DISPATCH((n) == 4, rb_define_module_function_04, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_03(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_05(n) RBIMPL_ANYARGS_DISPATCH((n) == 5, rb_define_module_function_05, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_04(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_06(n) RBIMPL_ANYARGS_DISPATCH((n) == 6, rb_define_module_function_06, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_05(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_07(n) RBIMPL_ANYARGS_DISPATCH((n) == 7, rb_define_module_function_07, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_06(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_08(n) RBIMPL_ANYARGS_DISPATCH((n) == 8, rb_define_module_function_08, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_07(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_09(n) RBIMPL_ANYARGS_DISPATCH((n) == 9, rb_define_module_function_09, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_08(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_10(n) RBIMPL_ANYARGS_DISPATCH((n) == 10, rb_define_module_function_10, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_09(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_11(n) RBIMPL_ANYARGS_DISPATCH((n) == 11, rb_define_module_function_11, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_10(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_12(n) RBIMPL_ANYARGS_DISPATCH((n) == 12, rb_define_module_function_12, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_11(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_module_function_13, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_12(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_module_function_14, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_13(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_module_function_15, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_14(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_m2(n) RBIMPL_ANYARGS_DISPATCH((n) == -2, rb_define_global_function_m2, rb_define_global_function_m3)
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_m1(n) RBIMPL_ANYARGS_DISPATCH((n) == -1, rb_define_global_function_m1, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_m2(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_00(n) RBIMPL_ANYARGS_DISPATCH((n) == 0, rb_define_global_function_00, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_m1(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_01(n) RBIMPL_ANYARGS_DISPATCH((n) == 1, rb_define_global_function_01, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_00(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_02(n) RBIMPL_ANYARGS_DISPATCH((n) == 2, rb_define_global_function_02, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_01(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_03(n) RBIMPL_ANYARGS_DISPATCH((n) == 3, rb_define_global_function_03, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_02(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_04(n) RBIMPL_ANYARGS_DISPATCH((n) == 4, rb_define_global_function_04, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_03(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_05(n) RBIMPL_ANYARGS_DISPATCH((n) == 5, rb_define_global_function_05, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_04(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_06(n) RBIMPL_ANYARGS_DISPATCH((n) == 6, rb_define_global_function_06, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_05(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_07(n) RBIMPL_ANYARGS_DISPATCH((n) == 7, rb_define_global_function_07, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_06(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_08(n) RBIMPL_ANYARGS_DISPATCH((n) == 8, rb_define_global_function_08, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_07(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_09(n) RBIMPL_ANYARGS_DISPATCH((n) == 9, rb_define_global_function_09, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_08(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_10(n) RBIMPL_ANYARGS_DISPATCH((n) == 10, rb_define_global_function_10, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_09(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_11(n) RBIMPL_ANYARGS_DISPATCH((n) == 11, rb_define_global_function_11, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_10(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_12(n) RBIMPL_ANYARGS_DISPATCH((n) == 12, rb_define_global_function_12, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_11(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_global_function_13, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_12(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_global_function_14, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_13(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_global_function_15, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_14(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_m2(n) RBIMPL_ANYARGS_DISPATCH((n) == -2, rb_define_method_id_m2, rb_define_method_id_m3)
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_m1(n) RBIMPL_ANYARGS_DISPATCH((n) == -1, rb_define_method_id_m1, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_m2(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_00(n) RBIMPL_ANYARGS_DISPATCH((n) == 0, rb_define_method_id_00, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_m1(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_01(n) RBIMPL_ANYARGS_DISPATCH((n) == 1, rb_define_method_id_01, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_00(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_02(n) RBIMPL_ANYARGS_DISPATCH((n) == 2, rb_define_method_id_02, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_01(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_03(n) RBIMPL_ANYARGS_DISPATCH((n) == 3, rb_define_method_id_03, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_02(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_04(n) RBIMPL_ANYARGS_DISPATCH((n) == 4, rb_define_method_id_04, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_03(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_05(n) RBIMPL_ANYARGS_DISPATCH((n) == 5, rb_define_method_id_05, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_04(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_06(n) RBIMPL_ANYARGS_DISPATCH((n) == 6, rb_define_method_id_06, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_05(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_07(n) RBIMPL_ANYARGS_DISPATCH((n) == 7, rb_define_method_id_07, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_06(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_08(n) RBIMPL_ANYARGS_DISPATCH((n) == 8, rb_define_method_id_08, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_07(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_09(n) RBIMPL_ANYARGS_DISPATCH((n) == 9, rb_define_method_id_09, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_08(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_10(n) RBIMPL_ANYARGS_DISPATCH((n) == 10, rb_define_method_id_10, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_09(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_11(n) RBIMPL_ANYARGS_DISPATCH((n) == 11, rb_define_method_id_11, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_10(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_12(n) RBIMPL_ANYARGS_DISPATCH((n) == 12, rb_define_method_id_12, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_11(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_method_id_13, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_12(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_method_id_14, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_13(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_method_id_15, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_14(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_m2(n) RBIMPL_ANYARGS_DISPATCH((n) == -2, rb_define_method_m2, rb_define_method_m3)
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_m1(n) RBIMPL_ANYARGS_DISPATCH((n) == -1, rb_define_method_m1, RBIMPL_ANYARGS_DISPATCH_rb_define_method_m2(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_00(n) RBIMPL_ANYARGS_DISPATCH((n) == 0, rb_define_method_00, RBIMPL_ANYARGS_DISPATCH_rb_define_method_m1(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_01(n) RBIMPL_ANYARGS_DISPATCH((n) == 1, rb_define_method_01, RBIMPL_ANYARGS_DISPATCH_rb_define_method_00(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_02(n) RBIMPL_ANYARGS_DISPATCH((n) == 2, rb_define_method_02, RBIMPL_ANYARGS_DISPATCH_rb_define_method_01(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_03(n) RBIMPL_ANYARGS_DISPATCH((n) == 3, rb_define_method_03, RBIMPL_ANYARGS_DISPATCH_rb_define_method_02(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_04(n) RBIMPL_ANYARGS_DISPATCH((n) == 4, rb_define_method_04, RBIMPL_ANYARGS_DISPATCH_rb_define_method_03(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_05(n) RBIMPL_ANYARGS_DISPATCH((n) == 5, rb_define_method_05, RBIMPL_ANYARGS_DISPATCH_rb_define_method_04(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_06(n) RBIMPL_ANYARGS_DISPATCH((n) == 6, rb_define_method_06, RBIMPL_ANYARGS_DISPATCH_rb_define_method_05(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_07(n) RBIMPL_ANYARGS_DISPATCH((n) == 7, rb_define_method_07, RBIMPL_ANYARGS_DISPATCH_rb_define_method_06(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_08(n) RBIMPL_ANYARGS_DISPATCH((n) == 8, rb_define_method_08, RBIMPL_ANYARGS_DISPATCH_rb_define_method_07(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_09(n) RBIMPL_ANYARGS_DISPATCH((n) == 9, rb_define_method_09, RBIMPL_ANYARGS_DISPATCH_rb_define_method_08(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_10(n) RBIMPL_ANYARGS_DISPATCH((n) == 10, rb_define_method_10, RBIMPL_ANYARGS_DISPATCH_rb_define_method_09(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_11(n) RBIMPL_ANYARGS_DISPATCH((n) == 11, rb_define_method_11, RBIMPL_ANYARGS_DISPATCH_rb_define_method_10(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_12(n) RBIMPL_ANYARGS_DISPATCH((n) == 12, rb_define_method_12, RBIMPL_ANYARGS_DISPATCH_rb_define_method_11(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n) RBIMPL_ANYARGS_DISPATCH((n) == 13, rb_define_method_13, RBIMPL_ANYARGS_DISPATCH_rb_define_method_12(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n) RBIMPL_ANYARGS_DISPATCH((n) == 14, rb_define_method_14, RBIMPL_ANYARGS_DISPATCH_rb_define_method_13(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n) RBIMPL_ANYARGS_DISPATCH((n) == 15, rb_define_method_15, RBIMPL_ANYARGS_DISPATCH_rb_define_method_14(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_singleton_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_protected_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_private_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_private_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_private_method_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_module_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_module_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_module_function_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_global_function(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_global_function_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_global_function_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method_id(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_id_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_id_15(n))
+# define RBIMPL_ANYARGS_DISPATCH_rb_define_method(n, f) RBIMPL_ANYARGS_DISPATCH(RBIMPL_CFUNC_IS_rb_f_notimplement(f), rb_define_method_notimpl, RBIMPL_ANYARGS_DISPATCH_rb_define_method_15(n))
+# define RBIMPL_ANYARGS_ATTRSET(sym) RBIMPL_ATTR_MAYBE_UNUSED() RBIMPL_ATTR_NONNULL(()) RBIMPL_ATTR_WEAKREF(sym)
+# define RBIMPL_ANYARGS_DECL(sym, ...) \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _notimpl(__VA_ARGS__, VALUE(*)(int, const VALUE *, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m3(__VA_ARGS__, VALUE(*)(ANYARGS), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m2(__VA_ARGS__, VALUE(*)(VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _m1(__VA_ARGS__, VALUE(*)(int, union { VALUE *x; const VALUE *y; } __attribute__((__transparent_union__)), VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _00(__VA_ARGS__, VALUE(*)(VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _01(__VA_ARGS__, VALUE(*)(VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _02(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _03(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _04(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _05(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _06(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _07(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _08(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _09(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _10(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _11(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _12(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _13(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _14(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int); \
+RBIMPL_ANYARGS_ATTRSET(sym) static void sym ## _15(__VA_ARGS__, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE), int);
+RBIMPL_ANYARGS_DECL(rb_define_singleton_method, VALUE, const char *)
+RBIMPL_ANYARGS_DECL(rb_define_protected_method, VALUE, const char *)
+RBIMPL_ANYARGS_DECL(rb_define_private_method, VALUE, const char *)
+RBIMPL_ANYARGS_DECL(rb_define_module_function, VALUE, const char *)
+RBIMPL_ANYARGS_DECL(rb_define_global_function, const char *)
+RBIMPL_ANYARGS_DECL(rb_define_method_id, VALUE, ID)
+RBIMPL_ANYARGS_DECL(rb_define_method, VALUE, const char *)
+/** @endcond */
+
+/**
+ * @brief Defines klass\#mid.
+ * @see ::rb_define_method
+ * @param klass Where the method lives.
+ * @param mid Name of the defining method.
+ * @param func Implementation of klass\#mid.
+ * @param arity Arity of klass\#mid.
+ */
+#define rb_define_method(klass, mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_method((arity), (func))((klass), (mid), (func), (arity))
+
+/**
+ * @brief Defines klass\#mid.
+ * @see ::rb_define_method_id
+ * @param klass Where the method lives.
+ * @param mid Name of the defining method.
+ * @param func Implementation of klass\#mid.
+ * @param arity Arity of klass\#mid.
+ */
+#define rb_define_method_id(klass, mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_method_id((arity), (func))((klass), (mid), (func), (arity))
+
+/**
+ * @brief Defines obj.mid.
+ * @see ::rb_define_singleton_method
+ * @param obj Where the method lives.
+ * @param mid Name of the defining method.
+ * @param func Implementation of obj.mid.
+ * @param arity Arity of obj.mid.
+ */
+#define rb_define_singleton_method(obj, mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_singleton_method((arity), (func))((obj), (mid), (func), (arity))
+
+/**
+ * @brief Defines klass\#mid and make it protected.
+ * @see ::rb_define_protected_method
+ * @param klass Where the method lives.
+ * @param mid Name of the defining method.
+ * @param func Implementation of klass\#mid.
+ * @param arity Arity of klass\#mid.
+ */
+#define rb_define_protected_method(klass, mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_protected_method((arity), (func))((klass), (mid), (func), (arity))
+
+/**
+ * @brief Defines klass\#mid and make it private.
+ * @see ::rb_define_private_method
+ * @param klass Where the method lives.
+ * @param mid Name of the defining method.
+ * @param func Implementation of klass\#mid.
+ * @param arity Arity of klass\#mid.
+ */
+#define rb_define_private_method(klass, mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_private_method((arity), (func))((klass), (mid), (func), (arity))
+
+/**
+ * @brief Defines mod\#mid and make it a module function.
+ * @see ::rb_define_module_function
+ * @param mod Where the method lives.
+ * @param mid Name of the defining method.
+ * @param func Implementation of mod\#mid.
+ * @param arity Arity of mod\#mid.
+ */
+#define rb_define_module_function(mod, mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_module_function((arity), (func))((mod), (mid), (func), (arity))
+
+/**
+ * @brief Defines ::rb_mKerbel \#mid.
+ * @see ::rb_define_global_function
+ * @param mid Name of the defining method.
+ * @param func Implementation of ::rb_mKernel \#mid.
+ * @param arity Arity of ::rb_mKernel \#mid.
+ */
+#define rb_define_global_function(mid, func, arity) RBIMPL_ANYARGS_DISPATCH_rb_define_global_function((arity), (func))((mid), (func), (arity))
+
+#endif /* __cplusplus */
+
+#if defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus)
+/* In C23, K&R style prototypes are gone and so `void foo(ANYARGS)` became
+ * equivalent to `void foo(void)` unlike in earlier versions. This is a problem
+ * for rb_define_* functions since that makes all valid functions one can pass
+ * trip -Wincompatible-pointer-types, which we treat as errors. This is mostly
+ * not a problem for the __builtin_choose_expr path, but outside of that we
+ * need to add a cast for compatibility.
+ */
+#define rb_define_method(klass, mid, func, arity) rb_define_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_method_id(klass, mid, func, arity) rb_define_method_id((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_singleton_method(obj, mid, func, arity) rb_define_singleton_method((obj), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_protected_method(klass, mid, func, arity) rb_define_protected_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_private_method(klass, mid, func, arity) rb_define_private_method((klass), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_module_function(mod, mid, func, arity) rb_define_module_function((mod), (mid), (VALUE (*)(ANYARGS))(func), (arity))
+#define rb_define_global_function(mid, func, arity) rb_define_global_function((mid), (VALUE (*)(ANYARGS))(func), (arity))
+
+#undef RBIMPL_CAST_FN_PTR
+#endif /* defined(RBIMPL_CAST_FN_PTR) && !defined(__cplusplus) */
+
+/**
+ * This macro is to properly cast a function parameter of *_define_method
+ * family. It has been around since 1.x era so you can maximise backwards
+ * compatibility by using it.
+ *
+ * ```CXX
+ * rb_define_method(klass, "method", RUBY_METHOD_FUNC(func), arity);
+ * ```
+ *
+ * @param func A pointer to a function that implements a method.
+ */
+#if ! defined(RUBY_DEVEL)
+# define RUBY_METHOD_FUNC(func) RBIMPL_CAST((VALUE (*)(ANYARGS))(func))
+
+#elif ! RUBY_DEVEL
+# define RUBY_METHOD_FUNC(func) RBIMPL_CAST((VALUE (*)(ANYARGS))(func))
+
+#elif ! defined(rb_define_method)
+# define RUBY_METHOD_FUNC(func) RBIMPL_CAST((VALUE (*)(ANYARGS))(func))
+
+#else
+# define RUBY_METHOD_FUNC(func) (func)
+
+#endif
+
+#endif /* RBIMPL_ANYARGS_H */
diff --git a/include/ruby/internal/arithmetic.h b/include/ruby/internal/arithmetic.h
new file mode 100644
index 0000000000..7ebb4a86f1
--- /dev/null
+++ b/include/ruby/internal/arithmetic.h
@@ -0,0 +1,39 @@
+#ifndef RBIMPL_ARITHMETIC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Conversion between C's arithmetic types and Ruby's numeric
+ * types.
+ */
+#include "ruby/internal/arithmetic/char.h"
+#include "ruby/internal/arithmetic/double.h"
+#include "ruby/internal/arithmetic/fixnum.h"
+#include "ruby/internal/arithmetic/gid_t.h"
+#include "ruby/internal/arithmetic/int.h"
+#include "ruby/internal/arithmetic/intptr_t.h"
+#include "ruby/internal/arithmetic/long.h"
+#include "ruby/internal/arithmetic/long_long.h"
+#include "ruby/internal/arithmetic/mode_t.h"
+#include "ruby/internal/arithmetic/off_t.h"
+#include "ruby/internal/arithmetic/pid_t.h"
+#include "ruby/internal/arithmetic/short.h"
+#include "ruby/internal/arithmetic/size_t.h"
+#include "ruby/internal/arithmetic/st_data_t.h"
+#include "ruby/internal/arithmetic/uid_t.h"
+#endif /* RBIMPL_ARITHMETIC_H */
diff --git a/include/ruby/internal/arithmetic/char.h b/include/ruby/internal/arithmetic/char.h
new file mode 100644
index 0000000000..920fdc0c9d
--- /dev/null
+++ b/include/ruby/internal/arithmetic/char.h
@@ -0,0 +1,81 @@
+#ifndef RBIMPL_ARITHMETIC_CHAR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_CHAR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `char` and Ruby's.
+ */
+#include "ruby/internal/arithmetic/int.h" /* NUM2INT is here, but */
+#include "ruby/internal/arithmetic/long.h" /* INT2FIX is here.*/
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/core/rstring.h"
+#include "ruby/internal/value_type.h"
+
+#define RB_NUM2CHR rb_num2char_inline /**< @alias{rb_num2char_inline} */
+#define NUM2CHR RB_NUM2CHR /**< @old{RB_NUM2CHR} */
+#define CHR2FIX RB_CHR2FIX /**< @old{RB_CHR2FIX} */
+
+/** @cond INTERNAL_MACRO */
+#define RB_CHR2FIX RB_CHR2FIX
+/** @endcond */
+
+RBIMPL_ATTR_CONST_UNLESS_DEBUG()
+RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(CXX14)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Converts a C's `unsigned char` into an instance of ::rb_cInteger.
+ *
+ * @param[in] c Arbitrary `unsigned char` value.
+ * @return An instance of ::rb_cInteger.
+ *
+ * @internal
+ *
+ * Nobody explicitly states this but in Ruby, a char means an unsigned integer
+ * value of range 0..255. This is a general principle. AFAIK there is no
+ * single line of code where char is signed.
+ */
+static inline VALUE
+RB_CHR2FIX(unsigned char c)
+{
+ return RB_INT2FIX(c);
+}
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `char`. At the same time it
+ * accepts a String of more than one character, and returns its first byte. In
+ * the early days there was a Ruby level "character" literal `?c`, which
+ * roughly worked this way.
+ *
+ * @param[in] x Either a string or a numeric.
+ * @exception rb_eTypeError `x` is not a numeric.
+ * @exception rb_eRangeError `x` is out of range of `unsigned int`.
+ * @return The passed value converted into C's `char`.
+ */
+static inline char
+rb_num2char_inline(VALUE x)
+{
+ if (RB_TYPE_P(x, RUBY_T_STRING) && (RSTRING_LEN(x)>=1))
+ return RSTRING_PTR(x)[0];
+ else
+ return RBIMPL_CAST((char)RB_NUM2INT(x));
+}
+
+#endif /* RBIMPL_ARITHMETIC_CHAR_H */
diff --git a/include/ruby/internal/arithmetic/double.h b/include/ruby/internal/arithmetic/double.h
new file mode 100644
index 0000000000..229de47aef
--- /dev/null
+++ b/include/ruby/internal/arithmetic/double.h
@@ -0,0 +1,72 @@
+#ifndef RBIMPL_ARITHMETIC_DOUBLE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_DOUBLE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `double` and Ruby's.
+ */
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+#define NUM2DBL rb_num2dbl /**< @old{rb_num2dbl} */
+#define RFLOAT_VALUE rb_float_value /**< @old{rb_float_value} */
+#define DBL2NUM rb_float_new /**< @old{rb_float_new} */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * Converts an instance of ::rb_cNumeric into C's `double`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @return The passed value converted into C's `double`.
+ */
+double rb_num2dbl(VALUE num);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Extracts its double value from an instance of ::rb_cFloat.
+ *
+ * @param[in] num An instance of ::rb_cFloat.
+ * @pre Must not pass anything other than a Fixnum.
+ * @return The passed value converted into C's `double`.
+ */
+double rb_float_value(VALUE num);
+
+/**
+ * Converts a C's `double` into an instance of ::rb_cFloat.
+ *
+ * @param[in] d Arbitrary `double` value.
+ * @return An instance of ::rb_cFloat.
+ */
+VALUE rb_float_new(double d);
+
+/**
+ * Identical to rb_float_new(), except it does not generate Flonums.
+ *
+ * @param[in] d Arbitrary `double` value.
+ * @return An instance of ::rb_cFloat.
+ *
+ * @internal
+ *
+ * @shyouhei has no idea why it is here.
+ */
+VALUE rb_float_new_in_heap(double d);
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_ARITHMETIC_DOUBLE_H */
diff --git a/include/ruby/internal/arithmetic/fixnum.h b/include/ruby/internal/arithmetic/fixnum.h
new file mode 100644
index 0000000000..c8927ac824
--- /dev/null
+++ b/include/ruby/internal/arithmetic/fixnum.h
@@ -0,0 +1,60 @@
+#ifndef RBIMPL_ARITHMETIC_FIXNUM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_FIXNUM_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Handling of integers formerly known as Fixnums.
+ */
+#include "ruby/backward/2/limits.h"
+
+#define FIXABLE RB_FIXABLE /**< @old{RB_FIXABLE} */
+#define FIXNUM_MAX RUBY_FIXNUM_MAX /**< @old{RUBY_FIXNUM_MAX} */
+#define FIXNUM_MIN RUBY_FIXNUM_MIN /**< @old{RUBY_FIXNUM_MIN} */
+#define NEGFIXABLE RB_NEGFIXABLE /**< @old{RB_NEGFIXABLE} */
+#define POSFIXABLE RB_POSFIXABLE /**< @old{RB_POSFIXABLE} */
+
+/**
+ * Checks if the passed value is in range of fixnum, assuming it is a positive
+ * number. Can sometimes be useful for C's unsigned integer types.
+ *
+ * @internal
+ *
+ * FIXABLE can be applied to anything, from double to intmax_t. The problem is
+ * double. On a 64bit system RUBY_FIXNUM_MAX is 4,611,686,018,427,387,903,
+ * which is not representable by a double. The nearest value that a double can
+ * represent is 4,611,686,018,427,387,904, which is not fixable. The
+ * seemingly-strange "< FIXNUM_MAX + 1" expression below is due to this.
+ */
+#define RB_POSFIXABLE(_) ((_) < RUBY_FIXNUM_MAX + 1)
+
+/**
+ * Checks if the passed value is in range of fixnum, assuming it is a negative
+ * number. This is an implementation of #RB_FIXABLE. Rarely used stand alone.
+ */
+#define RB_NEGFIXABLE(_) ((_) >= RUBY_FIXNUM_MIN)
+
+/** Checks if the passed value is in range of fixnum */
+#define RB_FIXABLE(_) (RB_POSFIXABLE(_) && RB_NEGFIXABLE(_))
+
+/** Maximum possible value that a fixnum can represent. */
+#define RUBY_FIXNUM_MAX (LONG_MAX / 2)
+
+/** Minimum possible value that a fixnum can represent. */
+#define RUBY_FIXNUM_MIN (LONG_MIN / 2)
+
+#endif /* RBIMPL_ARITHMETIC_FIXNUM_H */
diff --git a/include/ruby/internal/arithmetic/gid_t.h b/include/ruby/internal/arithmetic/gid_t.h
new file mode 100644
index 0000000000..361220bfab
--- /dev/null
+++ b/include/ruby/internal/arithmetic/gid_t.h
@@ -0,0 +1,41 @@
+#ifndef RBIMPL_ARITHMETIC_GID_T_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_GID_T_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `gid_t` and Ruby's.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/long.h"
+
+/** Converts a C's `gid_t` into an instance of ::rb_cInteger. */
+#ifndef GIDT2NUM
+# define GIDT2NUM RB_LONG2NUM
+#endif
+
+/** Converts an instance of ::rb_cNumeric into C's `gid_t`. */
+#ifndef NUM2GIDT
+# define NUM2GIDT RB_NUM2LONG
+#endif
+
+/** A rb_sprintf() format prefix to be used for a `gid_t` parameter. */
+#ifndef PRI_GIDT_PREFIX
+# define PRI_GIDT_PREFIX PRI_LONG_PREFIX
+#endif
+
+#endif /* RBIMPL_ARITHMETIC_GID_T_H */
diff --git a/include/ruby/internal/arithmetic/int.h b/include/ruby/internal/arithmetic/int.h
new file mode 100644
index 0000000000..7b24d16887
--- /dev/null
+++ b/include/ruby/internal/arithmetic/int.h
@@ -0,0 +1,264 @@
+#ifndef RBIMPL_ARITHMETIC_INT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_INT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `int` and Ruby's.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/fixnum.h"
+#include "ruby/internal/arithmetic/intptr_t.h"
+#include "ruby/internal/arithmetic/long.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/compiler_is.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/warning_push.h"
+#include "ruby/assert.h"
+
+#define RB_INT2NUM rb_int2num_inline /**< @alias{rb_int2num_inline} */
+#define RB_NUM2INT rb_num2int_inline /**< @alias{rb_num2int_inline} */
+#define RB_UINT2NUM rb_uint2num_inline /**< @alias{rb_uint2num_inline} */
+
+#define FIX2INT RB_FIX2INT /**< @old{RB_FIX2INT} */
+#define FIX2UINT RB_FIX2UINT /**< @old{RB_FIX2UINT} */
+#define INT2NUM RB_INT2NUM /**< @old{RB_INT2NUM} */
+#define NUM2INT RB_NUM2INT /**< @old{RB_NUM2INT} */
+#define NUM2UINT RB_NUM2UINT /**< @old{RB_NUM2UINT} */
+#define UINT2NUM RB_UINT2NUM /**< @old{RB_UINT2NUM} */
+
+/** @cond INTERNAL_MACRO */
+#define RB_FIX2INT RB_FIX2INT
+#define RB_NUM2UINT RB_NUM2UINT
+#define RB_FIX2UINT RB_FIX2UINT
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `long`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `int`.
+ * @return The passed value converted into C's `long`.
+ *
+ * @internal
+ *
+ * Yes, the API is really strange. It returns `long`, but raises when the
+ * value is out of `int`. This seems to be due to the fact that Matz favoured
+ * K&R before, and his machine at that moment was an ILP32 architecture.
+ */
+long rb_num2int(VALUE num);
+
+/**
+ * Identical to rb_num2int().
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `int`.
+ * @return The passed value converted into C's `long`.
+ *
+ * @internal
+ *
+ * This function seems to be a complete waste of disk space. @shyouhei has no
+ * idea why this is a different thing from rb_num2short().
+ */
+long rb_fix2int(VALUE num);
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `unsigned long`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `unsigned int`.
+ * @return The passed value converted into C's `unsigned long`.
+ *
+ * @internal
+ *
+ * Yes, the API is really strange. It returns `unsigned long`, but raises when
+ * the value is out of `unsigned int`. This seems to be due to the fact that
+ * Matz favoured K&R before, and his machine at that moment was an ILP32
+ * architecture.
+ */
+unsigned long rb_num2uint(VALUE num);
+
+/**
+ * Identical to rb_num2uint().
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `unsigned int`.
+ * @return The passed value converted into C's `unsigned long`.
+ *
+ * @internal
+ *
+ * This function seems to be a complete waste of disk space. @shyouhei has no
+ * idea why this is a different thing from rb_num2short().
+ */
+unsigned long rb_fix2uint(VALUE num);
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Converts a Fixnum into C's `int`.
+ *
+ * @param[in] x Some Fixnum.
+ * @pre Must not pass anything other than a Fixnum.
+ * @return The passed value converted into C's `int`.
+ */
+static inline int
+RB_FIX2INT(VALUE x)
+{
+ /* "FIX2INT raises a TypeError if passed nil", says rubyspec. Not sure if
+ * that is a desired behaviour but just preserve backwards compatilibily.
+ */
+#if 0
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x));
+#endif
+ long ret;
+
+ if /* constexpr */ (sizeof(int) < sizeof(long)) {
+ ret = rb_fix2int(x);
+ }
+ else {
+ ret = RB_FIX2LONG(x);
+ }
+
+ return RBIMPL_CAST((int)ret);
+}
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `int`.
+ *
+ * @param[in] x Something numeric.
+ * @exception rb_eTypeError `x` is not a numeric.
+ * @exception rb_eRangeError `x` is out of range of `int`.
+ * @return The passed value converted into C's `int`.
+ */
+static inline int
+rb_num2int_inline(VALUE x)
+{
+ long ret;
+
+ if /* constexpr */ (sizeof(int) == sizeof(long)) {
+ ret = RB_NUM2LONG(x);
+ }
+ else if (RB_FIXNUM_P(x)) {
+ ret = rb_fix2int(x);
+ }
+ else {
+ ret = rb_num2int(x);
+ }
+
+ return RBIMPL_CAST((int)ret);
+}
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `unsigned int`.
+ *
+ * @param[in] x Something numeric.
+ * @exception rb_eTypeError `x` is not a numeric.
+ * @exception rb_eRangeError `x` is out of range of `unsigned int`.
+ * @return The passed value converted into C's `unsigned int`.
+ */
+RBIMPL_ATTR_ARTIFICIAL()
+static inline unsigned int
+RB_NUM2UINT(VALUE x)
+{
+ unsigned long ret;
+
+ if /* constexpr */ (sizeof(int) < sizeof(long)) {
+ ret = rb_num2uint(x);
+ }
+ else {
+ ret = RB_NUM2ULONG(x);
+ }
+
+ return RBIMPL_CAST((unsigned int)ret);
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Converts a Fixnum into C's `int`.
+ *
+ * @param[in] x Some Fixnum.
+ * @pre Must not pass anything other than a Fixnum.
+ * @return The passed value converted into C's `int`.
+ */
+static inline unsigned int
+RB_FIX2UINT(VALUE x)
+{
+#if 0 /* Ditto for RB_FIX2INT. */
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x));
+#endif
+ unsigned long ret;
+
+ if /* constexpr */ (sizeof(int) < sizeof(long)) {
+ ret = rb_fix2uint(x);
+ }
+ else {
+ ret = RB_FIX2ULONG(x);
+ }
+
+ return RBIMPL_CAST((unsigned int)ret);
+}
+
+RBIMPL_WARNING_PUSH()
+#if RBIMPL_COMPILER_IS(GCC)
+RBIMPL_WARNING_IGNORED(-Wtype-limits) /* We can ignore them here. */
+#elif RBIMPL_HAS_WARNING("-Wtautological-constant-out-of-range-compare")
+RBIMPL_WARNING_IGNORED(-Wtautological-constant-out-of-range-compare)
+#endif
+
+/**
+ * Converts a C's `int` into an instance of ::rb_cInteger.
+ *
+ * @param[in] v Arbitrary `int` value.
+ * @return An instance of ::rb_cInteger.
+ */
+static inline VALUE
+rb_int2num_inline(int v)
+{
+ if (RB_FIXABLE(v))
+ return RB_INT2FIX(v);
+ else
+ return rb_int2big(v);
+}
+
+/**
+ * Converts a C's `unsigned int` into an instance of ::rb_cInteger.
+ *
+ * @param[in] v Arbitrary `unsigned int` value.
+ * @return An instance of ::rb_cInteger.
+ */
+static inline VALUE
+rb_uint2num_inline(unsigned int v)
+{
+ if (RB_POSFIXABLE(v))
+ return RB_LONG2FIX(RBIMPL_CAST((long)v));
+ else
+ return rb_uint2big(v);
+}
+
+RBIMPL_WARNING_POP()
+
+#endif /* RBIMPL_ARITHMETIC_INT_H */
diff --git a/include/ruby/internal/arithmetic/intptr_t.h b/include/ruby/internal/arithmetic/intptr_t.h
new file mode 100644
index 0000000000..70090f88e6
--- /dev/null
+++ b/include/ruby/internal/arithmetic/intptr_t.h
@@ -0,0 +1,86 @@
+#ifndef RBIMPL_ARITHMETIC_INTPTR_T_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_INTPTR_T_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `intptr_t` and Ruby's.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#include "ruby/internal/value.h"
+#include "ruby/internal/dllexport.h"
+
+#define rb_int_new rb_int2inum /**< @alias{rb_int2inum} */
+#define rb_uint_new rb_uint2inum /**< @alias{rb_uint2inum} */
+
+// These definitions are same as fiddle/conversions.h
+#if SIZEOF_VOIDP <= SIZEOF_LONG
+# define PTR2NUM(x) (LONG2NUM((long)(x)))
+# define NUM2PTR(x) ((void*)(NUM2ULONG(x)))
+#elif SIZEOF_VOIDP <= SIZEOF_LONG_LONG
+# define PTR2NUM(x) (LL2NUM((LONG_LONG)(x)))
+# define NUM2PTR(x) ((void*)(NUM2ULL(x)))
+#else
+// should have been an error in ruby/internal/value.h
+# error Need integer for VALUE
+#endif
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Converts a C's `intptr_t` into an instance of ::rb_cInteger.
+ *
+ * @param[in] i Arbitrary `intptr_t` value.
+ * @return An instance of ::rb_cInteger.
+ * @note This function always allocates Bignums, even if the given number
+ * is small enough to fit into a Fixnum.
+ */
+VALUE rb_int2big(intptr_t i);
+
+/**
+ * Converts a C's `intptr_t` into an instance of ::rb_cInteger.
+ *
+ * @param[in] i Arbitrary `intptr_t` value.
+ * @return An instance of ::rb_cInteger.
+ */
+VALUE rb_int2inum(intptr_t i);
+
+/**
+ * Converts a C's `intptr_t` into an instance of ::rb_cInteger.
+ *
+ * @param[in] i Arbitrary `intptr_t` value.
+ * @return An instance of ::rb_cInteger.
+ * @note This function always allocates Bignums, even if the given number
+ * is small enough to fit into a Fixnum.
+ */
+VALUE rb_uint2big(uintptr_t i);
+
+/**
+ * Converts a C's `uintptr_t` into an instance of ::rb_cInteger.
+ *
+ * @param[in] i Arbitrary `uintptr_t` value.
+ * @return An instance of ::rb_cInteger.
+ */
+VALUE rb_uint2inum(uintptr_t i);
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_ARITHMETIC_INTPTR_T_H */
diff --git a/include/ruby/internal/arithmetic/long.h b/include/ruby/internal/arithmetic/long.h
new file mode 100644
index 0000000000..6c00dbceb7
--- /dev/null
+++ b/include/ruby/internal/arithmetic/long.h
@@ -0,0 +1,356 @@
+#ifndef RBIMPL_ARITHMETIC_LONG_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_LONG_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `long` and Ruby's.
+ *
+ * ### Q&A ###
+ *
+ * - Q: Why are INT2FIX etc. here, not in `int.h`?
+ *
+ * - A: Because they are in fact handling `long`. It seems someone did not
+ * understand the difference of `int` and `long` when they designed those
+ * macros.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/fixnum.h" /* FIXABLE */
+#include "ruby/internal/arithmetic/intptr_t.h" /* rb_int2big etc.*/
+#include "ruby/internal/assume.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/special_consts.h" /* FIXNUM_FLAG */
+#include "ruby/internal/value.h"
+#include "ruby/assert.h"
+
+#define FIX2LONG RB_FIX2LONG /**< @old{RB_FIX2LONG} */
+#define FIX2ULONG RB_FIX2ULONG /**< @old{RB_FIX2ULONG} */
+#define INT2FIX RB_INT2FIX /**< @old{RB_INT2FIX} */
+#define LONG2FIX RB_INT2FIX /**< @old{RB_INT2FIX} */
+#define LONG2NUM RB_LONG2NUM /**< @old{RB_LONG2NUM} */
+#define NUM2LONG RB_NUM2LONG /**< @old{RB_NUM2LONG} */
+#define NUM2ULONG RB_NUM2ULONG /**< @old{RB_NUM2ULONG} */
+#define RB_FIX2LONG rb_fix2long /**< @alias{rb_fix2long} */
+#define RB_FIX2ULONG rb_fix2ulong /**< @alias{rb_fix2ulong} */
+#define RB_LONG2FIX RB_INT2FIX /**< @alias{RB_INT2FIX} */
+#define RB_LONG2NUM rb_long2num_inline /**< @alias{rb_long2num_inline} */
+#define RB_NUM2LONG rb_num2long_inline /**< @alias{rb_num2long_inline} */
+#define RB_NUM2ULONG rb_num2ulong_inline /**< @alias{rb_num2ulong_inline} */
+#define RB_ULONG2NUM rb_ulong2num_inline /**< @alias{rb_ulong2num_inline} */
+#define ULONG2NUM RB_ULONG2NUM /**< @old{RB_ULONG2NUM} */
+#define rb_fix_new RB_INT2FIX /**< @alias{RB_INT2FIX} */
+#define rb_long2int rb_long2int_inline /**< @alias{rb_long2int_inline} */
+
+/** @cond INTERNAL_MACRO */
+#define RB_INT2FIX RB_INT2FIX
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_COLD()
+/**
+ * This is an utility function to raise an ::rb_eRangeError.
+ *
+ * @param[in] num A signed value about to overflow.
+ * @exception rb_eRangeError `num` is out of range of `int`.
+ */
+void rb_out_of_int(SIGNED_VALUE num);
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `long`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `long`.
+ * @return The passed value converted into C's `long`.
+ */
+long rb_num2long(VALUE num);
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `unsigned long`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `unsigned long`.
+ * @return The passed value converted into C's `unsigned long`.
+ */
+unsigned long rb_num2ulong(VALUE num);
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_CONST_UNLESS_DEBUG()
+RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(CXX14)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Converts a C's `long` into an instance of ::rb_cInteger.
+ *
+ * @param[in] i Arbitrary `long` value.
+ * @return An instance of ::rb_cInteger.
+ */
+static inline VALUE
+RB_INT2FIX(long i)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(i));
+
+ /* :NOTE: VALUE can be wider than long. As j being unsigned, 2j+1 is fully
+ * defined. Also it can be compiled into a single LEA instruction. */
+ const unsigned long j = RBIMPL_CAST((unsigned long)i);
+ const unsigned long k = (j << 1) + RUBY_FIXNUM_FLAG;
+ const long l = RBIMPL_CAST((long)k);
+ const SIGNED_VALUE m = l; /* Sign extend */
+ const VALUE n = RBIMPL_CAST((VALUE)m);
+
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(n));
+ return n;
+}
+
+/**
+ * Checks if `int` can hold the given integer.
+ *
+ * @param[in] n Arbitrary `long` value.
+ * @exception rb_eRangeError `n` is out of range of `int`.
+ * @return Identical value of type `int`
+ */
+static inline int
+rb_long2int_inline(long n)
+{
+ int i = RBIMPL_CAST((int)n);
+
+ if /* constexpr */ (sizeof(long) <= sizeof(int)) {
+ RBIMPL_ASSUME(i == n);
+ }
+
+ if (i != n)
+ rb_out_of_int(n);
+
+ return i;
+}
+
+RBIMPL_ATTR_CONST_UNLESS_DEBUG()
+RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(CXX14)
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_fix2long(). People don't use it
+ * directly.
+ *
+ * @param[in] x A Fixnum.
+ * @return Identical value of type `long`
+ * @pre Must not pass anything other than a Fixnum.
+ */
+static inline long
+rbimpl_fix2long_by_idiv(VALUE x)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x));
+
+ /* :NOTE: VALUE can be wider than long. (x-1)/2 never overflows because
+ * RB_FIXNUM_P(x) holds. Also it has no portability issue like y>>1
+ * below. */
+ const SIGNED_VALUE y = RBIMPL_CAST((SIGNED_VALUE)(x - RUBY_FIXNUM_FLAG));
+ const SIGNED_VALUE z = y / 2;
+ const long w = RBIMPL_CAST((long)z);
+
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(w));
+ return w;
+}
+
+RBIMPL_ATTR_CONST_UNLESS_DEBUG()
+RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(CXX14)
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_fix2long(). People don't use it
+ * directly.
+ *
+ * @param[in] x A Fixnum.
+ * @return Identical value of type `long`
+ * @pre Must not pass anything other than a Fixnum.
+ */
+static inline long
+rbimpl_fix2long_by_shift(VALUE x)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x));
+
+ /* :NOTE: VALUE can be wider than long. If right shift is arithmetic, this
+ * is noticeably faster than above. */
+ const SIGNED_VALUE y = RBIMPL_CAST((SIGNED_VALUE)x);
+ const SIGNED_VALUE z = y >> 1;
+ const long w = RBIMPL_CAST((long)z);
+
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(w));
+ return w;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_fix2long(). People don't use it
+ * directly.
+ *
+ * @retval true This C compiler's right shift operator is arithmetic.
+ * @retval false This C compiler's right shift operator is logical.
+ */
+static inline bool
+rbimpl_right_shift_is_arithmetic_p(void)
+{
+ return (-1 >> 1) == -1;
+}
+
+RBIMPL_ATTR_CONST_UNLESS_DEBUG()
+RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(CXX14)
+/**
+ * Converts a Fixnum into C's `long`.
+ *
+ * @param[in] x Some Fixnum.
+ * @pre Must not pass anything other than a Fixnum.
+ * @return The passed value converted into C's `long`.
+ */
+static inline long
+rb_fix2long(VALUE x)
+{
+ if /* constexpr */ (rbimpl_right_shift_is_arithmetic_p()) {
+ return rbimpl_fix2long_by_shift(x);
+ }
+ else {
+ return rbimpl_fix2long_by_idiv(x);
+ }
+}
+
+RBIMPL_ATTR_CONST_UNLESS_DEBUG()
+RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(CXX14)
+/**
+ * Converts a Fixnum into C's `unsigned long`.
+ *
+ * @param[in] x Some Fixnum.
+ * @pre Must not pass anything other than a Fixnum.
+ * @return The passed value converted into C's `unsigned long`.
+ * @note Negative fixnums will be converted into large unsigned longs.
+ */
+static inline unsigned long
+rb_fix2ulong(VALUE x)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXNUM_P(x));
+ return RBIMPL_CAST((unsigned long)rb_fix2long(x));
+}
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `long`.
+ *
+ * @param[in] x Something numeric.
+ * @exception rb_eTypeError `x` is not a numeric.
+ * @exception rb_eRangeError `x` is out of range of `long`.
+ * @return The passed value converted into C's `long`.
+ */
+static inline long
+rb_num2long_inline(VALUE x)
+{
+ if (RB_FIXNUM_P(x))
+ return RB_FIX2LONG(x);
+ else
+ return rb_num2long(x);
+}
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `unsigned long`.
+ *
+ * @param[in] x Something numeric.
+ * @exception rb_eTypeError `x` is not a numeric.
+ * @exception rb_eRangeError `x` is out of range of `unsigned long`.
+ * @return The passed value converted into C's `unsigned long`.
+ *
+ * @internal
+ *
+ * This (negative fixnum would become a large unsigned long while negative
+ * bignum is an exception) has been THE behaviour of NUM2ULONG since the
+ * beginning. It is strange, but we can no longer change how it works at this
+ * moment. We have to get by with it.
+ *
+ * @see https://bugs.ruby-lang.org/issues/9089
+ */
+static inline unsigned long
+rb_num2ulong_inline(VALUE x)
+{
+ if (RB_FIXNUM_P(x))
+ return RB_FIX2ULONG(x);
+ else
+ return rb_num2ulong(x);
+}
+
+/**
+ * Converts a C's `long` into an instance of ::rb_cInteger.
+ *
+ * @param[in] v Arbitrary `long` value.
+ * @return An instance of ::rb_cInteger.
+ */
+static inline VALUE
+rb_long2num_inline(long v)
+{
+ if (RB_FIXABLE(v))
+ return RB_LONG2FIX(v);
+ else
+ return rb_int2big(v);
+}
+
+/**
+ * Converts a C's `unsigned long` into an instance of ::rb_cInteger.
+ *
+ * @param[in] v Arbitrary `unsigned long` value.
+ * @return An instance of ::rb_cInteger.
+ */
+static inline VALUE
+rb_ulong2num_inline(unsigned long v)
+{
+ if (RB_POSFIXABLE(v))
+ return RB_LONG2FIX(RBIMPL_CAST((long)v));
+ else
+ return rb_uint2big(v);
+}
+
+/**
+ * @cond INTERNAL_MACRO
+ *
+ * Following overload is necessary because sometimes INT2FIX is used as a enum
+ * value (e.g. `enum { FOO = INT2FIX(0) };`). THIS IS NG in theory because a
+ * VALUE does not fit into an enum (which must be a signed int). But we cannot
+ * break existing codes.
+ */
+#if RBIMPL_HAS_ATTR_CONSTEXPR_CXX14
+# /* C++ can write constexpr as enum values. */
+
+#elif ! defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
+# undef INT2FIX
+# define INT2FIX(i) (RBIMPL_CAST((VALUE)(i)) << 1 | RUBY_FIXNUM_FLAG)
+
+#else
+# undef INT2FIX
+# define INT2FIX(i) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(i), \
+ RBIMPL_CAST((VALUE)(i)) << 1 | RUBY_FIXNUM_FLAG, \
+ RB_INT2FIX(i))
+#endif
+/** @endcond */
+
+#endif /* RBIMPL_ARITHMETIC_LONG_H */
diff --git a/include/ruby/internal/arithmetic/long_long.h b/include/ruby/internal/arithmetic/long_long.h
new file mode 100644
index 0000000000..aab455c830
--- /dev/null
+++ b/include/ruby/internal/arithmetic/long_long.h
@@ -0,0 +1,135 @@
+#ifndef RBIMPL_ARITHMETIC_LONG_LONG_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_LONG_LONG_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `long long` and Ruby's.
+ */
+#include "ruby/internal/value.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/backward/2/long_long.h"
+
+#define RB_LL2NUM rb_ll2num_inline /**< @alias{rb_ll2num_inline} */
+#define RB_ULL2NUM rb_ull2num_inline /**< @alias{rb_ull2num_inline} */
+#define LL2NUM RB_LL2NUM /**< @old{RB_LL2NUM} */
+#define ULL2NUM RB_ULL2NUM /**< @old{RB_ULL2NUM} */
+#define RB_NUM2LL rb_num2ll_inline /**< @alias{rb_num2ll_inline} */
+#define RB_NUM2ULL rb_num2ull_inline /**< @alias{rb_num2ull_inline} */
+#define NUM2LL RB_NUM2LL /**< @old{RB_NUM2LL} */
+#define NUM2ULL RB_NUM2ULL /**< @old{RB_NUM2ULL} */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * Converts a C's `long long` into an instance of ::rb_cInteger.
+ *
+ * @param[in] num Arbitrary `long long` value.
+ * @return An instance of ::rb_cInteger.
+ */
+VALUE rb_ll2inum(LONG_LONG num);
+
+/**
+ * Converts a C's `unsigned long long` into an instance of ::rb_cInteger.
+ *
+ * @param[in] num Arbitrary `unsigned long long` value.
+ * @return An instance of ::rb_cInteger.
+ */
+VALUE rb_ull2inum(unsigned LONG_LONG num);
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `long long`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `long long`.
+ * @return The passed value converted into C's `long long`.
+ */
+LONG_LONG rb_num2ll(VALUE num);
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `unsigned long long`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `unsigned long long`.
+ * @return The passed value converted into C's `unsigned long long`.
+ */
+unsigned LONG_LONG rb_num2ull(VALUE num);
+RBIMPL_SYMBOL_EXPORT_END()
+
+/**
+ * Converts a C's `long long` into an instance of ::rb_cInteger.
+ *
+ * @param[in] n Arbitrary `long long` value.
+ * @return An instance of ::rb_cInteger
+ */
+static inline VALUE
+rb_ll2num_inline(LONG_LONG n)
+{
+ if (FIXABLE(n)) return LONG2FIX((long)n);
+ return rb_ll2inum(n);
+}
+
+/**
+ * Converts a C's `unsigned long long` into an instance of ::rb_cInteger.
+ *
+ * @param[in] n Arbitrary `unsigned long long` value.
+ * @return An instance of ::rb_cInteger
+ */
+static inline VALUE
+rb_ull2num_inline(unsigned LONG_LONG n)
+{
+ if (POSFIXABLE(n)) return LONG2FIX((long)n);
+ return rb_ull2inum(n);
+}
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `long long`.
+ *
+ * @param[in] x Something numeric.
+ * @exception rb_eTypeError `x` is not a numeric.
+ * @exception rb_eRangeError `x` is out of range of `long long`.
+ * @return The passed value converted into C's `long long`.
+ */
+static inline LONG_LONG
+rb_num2ll_inline(VALUE x)
+{
+ if (RB_FIXNUM_P(x))
+ return RB_FIX2LONG(x);
+ else
+ return rb_num2ll(x);
+}
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `unsigned long long`.
+ *
+ * @param[in] x Something numeric.
+ * @exception rb_eTypeError `x` is not a numeric.
+ * @exception rb_eRangeError `x` is out of range of `unsigned long long`.
+ * @return The passed value converted into C's `unsigned long long`.
+ */
+static inline unsigned LONG_LONG
+rb_num2ull_inline(VALUE x)
+{
+ if (RB_FIXNUM_P(x))
+ return RBIMPL_CAST((unsigned LONG_LONG)RB_FIX2LONG(x));
+ else
+ return rb_num2ull(x);
+}
+
+#endif /* RBIMPL_ARITHMETIC_LONG_LONG_H */
diff --git a/include/ruby/internal/arithmetic/mode_t.h b/include/ruby/internal/arithmetic/mode_t.h
new file mode 100644
index 0000000000..5b7ad35fbc
--- /dev/null
+++ b/include/ruby/internal/arithmetic/mode_t.h
@@ -0,0 +1,41 @@
+#ifndef RBIMPL_ARITHMETIC_MODE_T_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_MODE_T_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `mode_t` and Ruby's.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/int.h"
+
+/** Converts a C's `mode_t` into an instance of ::rb_cInteger. */
+#ifndef NUM2MODET
+# define NUM2MODET RB_NUM2INT
+#endif
+
+/** Converts an instance of ::rb_cNumeric into C's `mode_t`. */
+#ifndef MODET2NUM
+# define MODET2NUM RB_INT2NUM
+#endif
+
+/** A rb_sprintf() format prefix to be used for a `mode_t` parameter. */
+#ifndef PRI_MODET_PREFIX
+# define PRI_MODET_PREFIX PRI_INT_PREFIX
+#endif
+
+#endif /* RBIMPL_ARITHMETIC_MODE_T_H */
diff --git a/include/ruby/internal/arithmetic/off_t.h b/include/ruby/internal/arithmetic/off_t.h
new file mode 100644
index 0000000000..0ec9362cc9
--- /dev/null
+++ b/include/ruby/internal/arithmetic/off_t.h
@@ -0,0 +1,62 @@
+#ifndef RBIMPL_ARITHMETIC_OFF_T_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_OFF_T_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `off_t` and Ruby's.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/int.h"
+#include "ruby/internal/arithmetic/long.h"
+#include "ruby/internal/arithmetic/long_long.h"
+#include "ruby/backward/2/long_long.h"
+
+/** Converts a C's `off_t` into an instance of ::rb_cInteger. */
+#ifdef OFFT2NUM
+# /* take that. */
+#elif SIZEOF_OFF_T == SIZEOF_LONG_LONG
+# define OFFT2NUM RB_LL2NUM
+#elif SIZEOF_OFF_T == SIZEOF_LONG
+# define OFFT2NUM RB_LONG2NUM
+#else
+# define OFFT2NUM RB_INT2NUM
+#endif
+
+/** Converts an instance of ::rb_cNumeric into C's `off_t`. */
+#ifdef NUM2OFFT
+# /* take that. */
+#elif SIZEOF_OFF_T == SIZEOF_LONG_LONG
+# define NUM2OFFT RB_NUM2LL
+#elif SIZEOF_OFF_T == SIZEOF_LONG
+# define NUM2OFFT RB_NUM2LONG
+#else
+# define NUM2OFFT RB_NUM2INT
+#endif
+
+/** A rb_sprintf() format prefix to be used for an `off_t` parameter. */
+#ifdef PRI_OFFT_PREFIX
+# /* take that. */
+#elif SIZEOF_OFF_T == SIZEOF_LONG_LONG
+# define PRI_OFFT_PREFIX PRI_LL_PREFIX
+#elif SIZEOF_OFF_T == SIZEOF_LONG
+# define PRI_OFFT_PREFIX PRI_LONG_PREFIX
+#else
+# define PRI_OFFT_PREFIX PRI_INT_PREFIX
+#endif
+
+#endif /* RBIMPL_ARITHMETIC_OFF_T_H */
diff --git a/include/ruby/internal/arithmetic/pid_t.h b/include/ruby/internal/arithmetic/pid_t.h
new file mode 100644
index 0000000000..df9704e8f5
--- /dev/null
+++ b/include/ruby/internal/arithmetic/pid_t.h
@@ -0,0 +1,41 @@
+#ifndef RBIMPL_ARITHMETIC_PID_T_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_PID_T_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `pid_t` and Ruby's.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/long.h"
+
+/** Converts a C's `pid_t` into an instance of ::rb_cInteger. */
+#ifndef PIDT2NUM
+# define PIDT2NUM RB_LONG2NUM
+#endif
+
+/** Converts an instance of ::rb_cNumeric into C's `pid_t`. */
+#ifndef NUM2PIDT
+# define NUM2PIDT RB_NUM2LONG
+#endif
+
+/** A rb_sprintf() format prefix to be used for a `pid_t` parameter. */
+#ifndef PRI_PIDT_PREFIX
+# define PRI_PIDT_PREFIX PRI_LONG_PREFIX
+#endif
+
+#endif /* RBIMPL_ARITHMETIC_PID_T_H */
diff --git a/include/ruby/internal/arithmetic/short.h b/include/ruby/internal/arithmetic/short.h
new file mode 100644
index 0000000000..7a324d945b
--- /dev/null
+++ b/include/ruby/internal/arithmetic/short.h
@@ -0,0 +1,113 @@
+#ifndef RBIMPL_ARITHMETIC_SHORT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_SHORT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `short` and Ruby's.
+ *
+ * Shyouhei wonders: why there is no SHORT2NUM, given there are both
+ * #USHORT2NUM and #CHR2FIX?
+ */
+#include "ruby/internal/value.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/special_consts.h"
+
+#define RB_NUM2SHORT rb_num2short_inline /**< @alias{rb_num2short_inline} */
+#define RB_NUM2USHORT rb_num2ushort /**< @alias{rb_num2ushort} */
+#define NUM2SHORT RB_NUM2SHORT /**< @old{RB_NUM2SHORT} */
+#define NUM2USHORT RB_NUM2USHORT /**< @old{RB_NUM2USHORT} */
+#define USHORT2NUM RB_INT2FIX /**< @old{RB_INT2FIX} */
+#define RB_FIX2SHORT rb_fix2short /**< @alias{rb_fix2ushort} */
+#define FIX2SHORT RB_FIX2SHORT /**< @old{RB_FIX2SHORT} */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `short`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `short`.
+ * @return The passed value converted into C's `short`.
+ */
+short rb_num2short(VALUE num);
+
+/**
+ * Converts an instance of ::rb_cNumeric into C's `unsigned short`.
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `unsigned short`.
+ * @return The passed value converted into C's `unsigned short`.
+ */
+unsigned short rb_num2ushort(VALUE num);
+
+/**
+ * Identical to rb_num2short().
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `short`.
+ * @return The passed value converted into C's `short`.
+ *
+ * @internal
+ *
+ * This function seems to be a complete waste of disk space. @shyouhei has no
+ * idea why this is a different thing from rb_num2short().
+ */
+short rb_fix2short(VALUE num);
+
+/**
+ * Identical to rb_num2ushort().
+ *
+ * @param[in] num Something numeric.
+ * @exception rb_eTypeError `num` is not a numeric.
+ * @exception rb_eRangeError `num` is out of range of `unsigned short`.
+ * @return The passed value converted into C's `unsigned short`.
+ *
+ * @internal
+ *
+ * This function seems to be a complete waste of disk space. @shyouhei has no
+ * idea why this is a different thing from rb_num2ushort().
+ */
+unsigned short rb_fix2ushort(VALUE num);
+RBIMPL_SYMBOL_EXPORT_END()
+
+/**
+ * Identical to rb_num2short().
+ *
+ * @param[in] x Something numeric.
+ * @exception rb_eTypeError `x` is not a numeric.
+ * @exception rb_eRangeError `x` is out of range of `short`.
+ * @return The passed value converted into C's `short`.
+ *
+ * @internal
+ *
+ * This function seems to be a complete waste of time. @shyouhei has no idea
+ * why this is a different thing from rb_num2short().
+ */
+static inline short
+rb_num2short_inline(VALUE x)
+{
+ if (RB_FIXNUM_P(x))
+ return rb_fix2short(x);
+ else
+ return rb_num2short(x);
+}
+
+#endif /* RBIMPL_ARITHMETIC_SHORT_H */
diff --git a/include/ruby/internal/arithmetic/size_t.h b/include/ruby/internal/arithmetic/size_t.h
new file mode 100644
index 0000000000..1082160b8e
--- /dev/null
+++ b/include/ruby/internal/arithmetic/size_t.h
@@ -0,0 +1,66 @@
+#ifndef RBIMPL_ARITHMETIC_SIZE_T_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_SIZE_T_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `size_t` and Ruby's.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/int.h"
+#include "ruby/internal/arithmetic/long.h"
+#include "ruby/internal/arithmetic/long_long.h"
+#include "ruby/backward/2/long_long.h"
+
+#if defined(__DOXYGEN__)
+# /** Converts a C's `size_t` into an instance of ::rb_cInteger. */
+# define RB_SIZE2NUM RB_ULONG2NUM
+# /** Converts a C's `ssize_t` into an instance of ::rb_cInteger. */
+# define RB_SSIZE2NUM RB_LONG2NUM
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
+# define RB_SIZE2NUM RB_ULL2NUM
+# define RB_SSIZE2NUM RB_LL2NUM
+#elif SIZEOF_SIZE_T == SIZEOF_LONG
+# define RB_SIZE2NUM RB_ULONG2NUM
+# define RB_SSIZE2NUM RB_LONG2NUM
+#else
+# define RB_SIZE2NUM RB_UINT2NUM
+# define RB_SSIZE2NUM RB_INT2NUM
+#endif
+
+#if defined(__DOXYGEN__)
+# /** Converts an instance of ::rb_cInteger into C's `size_t`. */
+# define RB_NUM2SIZE RB_NUM2ULONG
+# /** Converts an instance of ::rb_cInteger into C's `ssize_t`. */
+# define RB_NUM2SSIZE RB_NUM2LONG
+#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
+# define RB_NUM2SIZE RB_NUM2ULL
+# define RB_NUM2SSIZE RB_NUM2LL
+#elif SIZEOF_SIZE_T == SIZEOF_LONG
+# define RB_NUM2SIZE RB_NUM2ULONG
+# define RB_NUM2SSIZE RB_NUM2LONG
+#else
+# define RB_NUM2SIZE RB_NUM2UINT
+# define RB_NUM2SSIZE RB_NUM2INT
+#endif
+
+#define NUM2SIZET RB_NUM2SIZE /**< @old{RB_NUM2SIZE} */
+#define SIZET2NUM RB_SIZE2NUM /**< @old{RB_SIZE2NUM} */
+#define NUM2SSIZET RB_NUM2SSIZE /**< @old{RB_NUM2SSIZE} */
+#define SSIZET2NUM RB_SSIZE2NUM /**< @old{RB_SSIZE2NUM} */
+
+#endif /* RBIMPL_ARITHMETIC_SIZE_T_H */
diff --git a/include/ruby/internal/arithmetic/st_data_t.h b/include/ruby/internal/arithmetic/st_data_t.h
new file mode 100644
index 0000000000..91776b3fce
--- /dev/null
+++ b/include/ruby/internal/arithmetic/st_data_t.h
@@ -0,0 +1,75 @@
+#ifndef RBIMPL_ARITHMERIC_ST_DATA_T_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMERIC_ST_DATA_T_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `st_data_t` and Ruby's.
+ */
+#include "ruby/internal/arithmetic/fixnum.h"
+#include "ruby/internal/arithmetic/long.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/value.h"
+#include "ruby/assert.h"
+#include "ruby/st.h"
+
+#define ST2FIX RB_ST2FIX /**< @old{RB_ST2FIX} */
+/** @cond INTERNAL_MACRO */
+#define RB_ST2FIX RB_ST2FIX
+/** @endcond */
+
+RBIMPL_ATTR_CONST_UNLESS_DEBUG()
+RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(CXX14)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Converts a C's `st_data_t` into an instance of ::rb_cInteger.
+ *
+ * @param[in] i The data in question.
+ * @return A converted result
+ * @warning THIS CONVERSION LOSES DATA! Be warned.
+ * @see https://bugs.ruby-lang.org/issues/13877
+ * @see https://bugs.ruby-lang.org/issues/14218
+ *
+ * @internal
+ *
+ * This is needed because of hash functions. Hash functions return
+ * `st_data_t`, which could theoretically be bigger than Fixnums. However
+ * allocating Bignums for them every time we calculate hash values is just too
+ * heavy. To avoid penalty we need to ignore some upper bit(s) and stick to
+ * Fixnums. This function is used for that purpose.
+ */
+static inline VALUE
+RB_ST2FIX(st_data_t i)
+{
+ SIGNED_VALUE x = RBIMPL_CAST((SIGNED_VALUE)i);
+
+ if (x >= 0) {
+ x &= RUBY_FIXNUM_MAX;
+ }
+ else {
+ x |= RUBY_FIXNUM_MIN;
+ }
+
+ RBIMPL_ASSERT_OR_ASSUME(RB_FIXABLE(x));
+ unsigned long y = RBIMPL_CAST((unsigned long)x);
+ return RB_LONG2FIX(RBIMPL_CAST((long)y));
+}
+
+#endif /* RBIMPL_ARITHMETIC_ST_DATA_T_H */
diff --git a/include/ruby/internal/arithmetic/uid_t.h b/include/ruby/internal/arithmetic/uid_t.h
new file mode 100644
index 0000000000..12cde2a9c8
--- /dev/null
+++ b/include/ruby/internal/arithmetic/uid_t.h
@@ -0,0 +1,41 @@
+#ifndef RBIMPL_ARITHMETIC_UID_T_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ARITHMETIC_UID_T_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Arithmetic conversion between C's `uid_t` and Ruby's.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/long.h"
+
+/** Converts a C's `uid_t` into an instance of ::rb_cInteger. */
+#ifndef UIDT2NUM
+# define UIDT2NUM RB_LONG2NUM
+#endif
+
+/** Converts an instance of ::rb_cNumeric into C's `uid_t`. */
+#ifndef NUM2UIDT
+# define NUM2UIDT RB_NUM2LONG
+#endif
+
+/** A rb_sprintf() format prefix to be used for a `uid_t` parameter. */
+#ifndef PRI_UIDT_PREFIX
+# define PRI_UIDT_PREFIX PRI_LONG_PREFIX
+#endif
+
+#endif /* RBIMPL_ARITHMETIC_UID_T_H */
diff --git a/include/ruby/internal/assume.h b/include/ruby/internal/assume.h
new file mode 100644
index 0000000000..4c183e8af9
--- /dev/null
+++ b/include/ruby/internal/assume.h
@@ -0,0 +1,87 @@
+#ifndef RBIMPL_ASSUME_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ASSUME_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ASSUME / #RBIMPL_UNREACHABLE.
+ *
+ * These macros must be defined at once because:
+ *
+ * - #RBIMPL_ASSUME could fallback to #RBIMPL_UNREACHABLE.
+ * - #RBIMPL_UNREACHABLE could fallback to #RBIMPL_ASSUME.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/builtin.h"
+#include "ruby/internal/warning_push.h"
+
+/** @cond INTERNAL_MACRO */
+#if defined(HAVE___ASSUME)
+# define RBIMPL_HAVE___ASSUME
+#endif
+/** @endcond */
+
+/** Wraps (or simulates) `__builtin_unreachable`. */
+#if RBIMPL_HAS_BUILTIN(__builtin_unreachable)
+# define RBIMPL_UNREACHABLE_RETURN(_) __builtin_unreachable()
+
+#elif defined(RBIMPL_HAVE___ASSUME)
+# define RBIMPL_UNREACHABLE_RETURN(_) return (__assume(0), (_))
+
+#else
+# define RBIMPL_UNREACHABLE_RETURN(_) return (_)
+#endif
+
+/** Wraps (or simulates) `__builtin_unreachable`. */
+#if RBIMPL_HAS_BUILTIN(__builtin_unreachable)
+# define RBIMPL_UNREACHABLE __builtin_unreachable
+
+#elif defined(RBIMPL_HAVE___ASSUME)
+# define RBIMPL_UNREACHABLE() __assume(0)
+#endif
+
+/** Wraps (or simulates) `__assume`. */
+#if RBIMPL_COMPILER_SINCE(Intel, 13, 0, 0)
+# /* icc warnings are false positives. Ignore them. */
+# /* "warning #2261: __assume expression with side effects discarded" */
+# define RBIMPL_ASSUME(expr) \
+ RBIMPL_WARNING_PUSH() \
+ RBIMPL_WARNING_IGNORED(2261) \
+ __assume(expr) \
+ RBIMPL_WARNING_POP()
+
+#elif defined(RBIMPL_HAVE___ASSUME)
+# define RBIMPL_ASSUME __assume
+
+#elif RBIMPL_HAS_BUILTIN(__builtin_assume)
+# define RBIMPL_ASSUME __builtin_assume
+
+#elif ! defined(RBIMPL_UNREACHABLE)
+# define RBIMPL_ASSUME(_) RBIMPL_CAST((void)(_))
+
+#else
+# define RBIMPL_ASSUME(_) \
+ (RB_LIKELY(!!(_)) ? RBIMPL_CAST((void)0) : RBIMPL_UNREACHABLE())
+#endif
+
+#if ! defined(RBIMPL_UNREACHABLE)
+# define RBIMPL_UNREACHABLE() RBIMPL_ASSUME(0)
+#endif
+
+#endif /* RBIMPL_ASSUME_H */
diff --git a/include/ruby/internal/attr/alloc_size.h b/include/ruby/internal/attr/alloc_size.h
new file mode 100644
index 0000000000..954a2010f6
--- /dev/null
+++ b/include/ruby/internal/attr/alloc_size.h
@@ -0,0 +1,32 @@
+#ifndef RBIMPL_ATTR_ALLOC_SIZE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_ALLOC_SIZE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_ALLOC_SIZE.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((alloc_size))` */
+#if RBIMPL_HAS_ATTRIBUTE(alloc_size)
+# define RBIMPL_ATTR_ALLOC_SIZE(tuple) __attribute__((__alloc_size__ tuple))
+#else
+# define RBIMPL_ATTR_ALLOC_SIZE(tuple) /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_ALLOC_SIZE_H */
diff --git a/include/ruby/internal/attr/artificial.h b/include/ruby/internal/attr/artificial.h
new file mode 100644
index 0000000000..ef5f36abff
--- /dev/null
+++ b/include/ruby/internal/attr/artificial.h
@@ -0,0 +1,46 @@
+#ifndef RBIMPL_ATTR_ARTIFICIAL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_ARTIFICIAL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_ARTIFICIAL.
+ *
+ * ### Q&A ###
+ *
+ * - Q: What is this attribute? I don't get what GCC manual is talking about.
+ *
+ * - A: In short it is an attribute to manipulate GDB backtraces. The
+ * attribute makes the best sense when it comes with
+ * __attribute__((always_inline)). When a function annotated with this
+ * attribute gets inlined, and when you somehow look at a backtrace which
+ * includes such inlined call site, then the backtrace shows the caller
+ * and not the callee. This is handy for instance when an identical
+ * function is inlined more than once in a single big function. On such
+ * case it gets vital to know where the inlining happened in the callee.
+ * See also https://stackoverflow.com/a/21936099
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((artificial))` */
+#if RBIMPL_HAS_ATTRIBUTE(artificial)
+# define RBIMPL_ATTR_ARTIFICIAL() __attribute__((__artificial__))
+#else
+# define RBIMPL_ATTR_ARTIFICIAL() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_ARTIFICIAL_H */
diff --git a/include/ruby/internal/attr/cold.h b/include/ruby/internal/attr/cold.h
new file mode 100644
index 0000000000..c68b3ae784
--- /dev/null
+++ b/include/ruby/internal/attr/cold.h
@@ -0,0 +1,37 @@
+#ifndef RBIMPL_ATTR_COLD_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_COLD_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_COLD.
+ */
+#include "ruby/internal/compiler_is.h"
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((cold))` */
+#if RBIMPL_COMPILER_IS(SunPro)
+# /* Recent SunPro has __has_attribute, and is broken. */
+# /* It reports it has attribute cold, reality isn't (warnings issued). */
+# define RBIMPL_ATTR_COLD() /* void */
+#elif RBIMPL_HAS_ATTRIBUTE(cold)
+# define RBIMPL_ATTR_COLD() __attribute__((__cold__))
+#else
+# define RBIMPL_ATTR_COLD() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_COLD_H */
diff --git a/include/ruby/internal/attr/const.h b/include/ruby/internal/attr/const.h
new file mode 100644
index 0000000000..e66aa17c70
--- /dev/null
+++ b/include/ruby/internal/attr/const.h
@@ -0,0 +1,46 @@
+#ifndef RBIMPL_ATTR_CONST_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_CONST_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_CONST.
+ */
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/has/declspec_attribute.h"
+
+/** Wraps (or simulates) `__attribute__((const))` */
+#if RBIMPL_HAS_ATTRIBUTE(const)
+# define RBIMPL_ATTR_CONST() __attribute__((__const__))
+#elif RBIMPL_HAS_DECLSPEC_ATTRIBUTE(noalias)
+# /* If a function can be a const, that is also a noalias. */
+# define RBIMPL_ATTR_CONST() __declspec(noalias)
+#elif RBIMPL_COMPILER_SINCE(SunPro, 5, 10, 0)
+# define RBIMPL_ATTR_CONST() _Pragma("no_side_effect")
+#else
+# define RBIMPL_ATTR_CONST() /* void */
+#endif
+
+/** Enables #RBIMPL_ATTR_CONST if and only if. ! #RUBY_DEBUG. */
+#if !defined(RUBY_DEBUG) || !RUBY_DEBUG
+# define RBIMPL_ATTR_CONST_UNLESS_DEBUG() RBIMPL_ATTR_CONST()
+#else
+# define RBIMPL_ATTR_CONST_UNLESS_DEBUG() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_CONST_H */
diff --git a/include/ruby/internal/attr/constexpr.h b/include/ruby/internal/attr/constexpr.h
new file mode 100644
index 0000000000..abc4f238b5
--- /dev/null
+++ b/include/ruby/internal/attr/constexpr.h
@@ -0,0 +1,84 @@
+#ifndef RBIMPL_ATTR_CONSTEXPR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_CONSTEXPR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief #RBIMPL_ATTR_CONSTEXPR.
+ */
+#include "ruby/internal/has/feature.h"
+#include "ruby/internal/compiler_is.h"
+
+/** @cond INTERNAL_MACRO */
+#if ! defined(__cplusplus)
+# /* Makes no sense. */
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX11 0
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX14 0
+
+#elif defined(__cpp_constexpr)
+# /* https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations */
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX11 (__cpp_constexpr >= 200704L)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX14 (__cpp_constexpr >= 201304L)
+
+#elif RBIMPL_COMPILER_SINCE(MSVC, 19, 0, 0)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX11 RBIMPL_COMPILER_SINCE(MSVC, 19, 00, 00)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX14 RBIMPL_COMPILER_SINCE(MSVC, 19, 11, 00)
+
+#elif RBIMPL_COMPILER_SINCE(SunPro, 5, 13, 0)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX11 (__cplusplus >= 201103L)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX14 (__cplusplus >= 201402L)
+
+#elif RBIMPL_COMPILER_SINCE(GCC, 4, 9, 0)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX11 (__cplusplus >= 201103L)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX14 (__cplusplus >= 201402L)
+
+#elif RBIMPL_HAS_FEATURE(cxx_relaxed_constexpr)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX11 1
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX14 1
+
+#elif RBIMPL_HAS_FEATURE(cxx_constexpr)
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX11 1
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX14 0
+
+#else
+# /* :FIXME: icpc must have constexpr but don't know how to detect. */
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX11 0
+# define RBIMPL_HAS_ATTR_CONSTEXPR_CXX14 0
+#endif
+/** @endcond */
+
+/** Wraps (or simulates) C++11 `constexpr`. */
+#if RBIMPL_HAS_ATTR_CONSTEXPR_CXX14
+# define RBIMPL_ATTR_CONSTEXPR(_) constexpr
+
+#elif RBIMPL_HAS_ATTR_CONSTEXPR_CXX11
+# define RBIMPL_ATTR_CONSTEXPR(_) RBIMPL_ATTR_CONSTEXPR_ ## _
+# define RBIMPL_ATTR_CONSTEXPR_CXX11 constexpr
+# define RBIMPL_ATTR_CONSTEXPR_CXX14 /* void */
+
+#else
+# define RBIMPL_ATTR_CONSTEXPR(_) /* void */
+#endif
+
+/** Enables #RBIMPL_ATTR_CONSTEXPR if and only if. ! #RUBY_DEBUG. */
+#if !RUBY_DEBUG
+# define RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(_) RBIMPL_ATTR_CONSTEXPR(_)
+#else
+# define RBIMPL_ATTR_CONSTEXPR_UNLESS_DEBUG(_) /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_CONSTEXPR_H */
diff --git a/include/ruby/internal/attr/deprecated.h b/include/ruby/internal/attr/deprecated.h
new file mode 100644
index 0000000000..a374ace868
--- /dev/null
+++ b/include/ruby/internal/attr/deprecated.h
@@ -0,0 +1,82 @@
+#ifndef RBIMPL_ATTR_DEPRECATED_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_DEPRECATED_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_DEPRECATED.
+ */
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/has/c_attribute.h"
+#include "ruby/internal/has/cpp_attribute.h"
+#include "ruby/internal/has/declspec_attribute.h"
+#include "ruby/internal/has/extension.h"
+
+/** Wraps (or simulates) `[[deprecated]]` */
+#if defined(__COVERITY__)
+/* Coverity Scan emulates gcc but seems not to support this attribute correctly */
+# define RBIMPL_ATTR_DEPRECATED(msg)
+
+#elif RBIMPL_HAS_EXTENSION(attribute_deprecated_with_message)
+# define RBIMPL_ATTR_DEPRECATED(msg) __attribute__((__deprecated__ msg))
+
+#elif defined(__cplusplus) && RBIMPL_COMPILER_SINCE(GCC, 10, 1, 0) && RBIMPL_COMPILER_BEFORE(GCC, 10, 3, 0)
+# /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95302 */
+# define RBIMPL_ATTR_DEPRECATED(msg) /* disable until they fix this bug */
+
+#elif RBIMPL_COMPILER_SINCE(GCC, 4, 5, 0)
+# define RBIMPL_ATTR_DEPRECATED(msg) __attribute__((__deprecated__ msg))
+
+#elif RBIMPL_COMPILER_SINCE(Intel, 13, 0, 0)
+# define RBIMPL_ATTR_DEPRECATED(msg) __attribute__((__deprecated__ msg))
+
+#elif RBIMPL_HAS_ATTRIBUTE(deprecated) /* but not with message. */
+# define RBIMPL_ATTR_DEPRECATED(msg) __attribute__((__deprecated__))
+
+#elif RBIMPL_COMPILER_IS(MSVC)
+# define RBIMPL_ATTR_DEPRECATED(msg) __declspec(deprecated msg)
+
+#elif RBIMPL_HAS_DECLSPEC_ATTRIBUTE(deprecated)
+# define RBIMPL_ATTR_DEPRECATED(msg) __declspec(deprecated)
+
+#elif RBIMPL_HAS_CPP_ATTRIBUTE(deprecated)
+# define RBIMPL_ATTR_DEPRECATED(msg) [[deprecated msg]]
+
+#elif RBIMPL_HAS_C_ATTRIBUTE(deprecated)
+# define RBIMPL_ATTR_DEPRECATED(msg) [[deprecated msg]]
+
+#else
+# define RBIMPL_ATTR_DEPRECATED(msg) /* void */
+#endif
+
+/** This is when a function is used internally (for backwards compatibility
+ * etc.), but extension libraries must consider it deprecated. */
+#if defined(RUBY_EXPORT)
+# define RBIMPL_ATTR_DEPRECATED_EXT(msg) /* void */
+#else
+# define RBIMPL_ATTR_DEPRECATED_EXT(msg) RBIMPL_ATTR_DEPRECATED(msg)
+#endif
+
+#define RBIMPL_ATTR_DEPRECATED_SINCE(ver) \
+ RBIMPL_ATTR_DEPRECATED(("since " #ver))
+#define RBIMPL_ATTR_DEPRECATED_INTERNAL(ver) \
+ RBIMPL_ATTR_DEPRECATED(("since "#ver", also internal"))
+#define RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY() \
+ RBIMPL_ATTR_DEPRECATED(("only for internal use"))
+
+#endif /* RBIMPL_ATTR_DEPRECATED_H */
diff --git a/include/ruby/internal/attr/diagnose_if.h b/include/ruby/internal/attr/diagnose_if.h
new file mode 100644
index 0000000000..cadc6ce258
--- /dev/null
+++ b/include/ruby/internal/attr/diagnose_if.h
@@ -0,0 +1,42 @@
+#ifndef RBIMPL_ATTR_DIAGNOSE_IF_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_DIAGNOSE_IF_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_DIAGNOSE_IF.
+ */
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/warning_push.h"
+
+/** Wraps (or simulates) `__attribute__((diagnose_if))` */
+#if RBIMPL_COMPILER_BEFORE(Clang, 5, 0, 0)
+# /* https://bugs.llvm.org/show_bug.cgi?id=34319 */
+# define RBIMPL_ATTR_DIAGNOSE_IF(_, __, ___) /* void */
+
+#elif RBIMPL_HAS_ATTRIBUTE(diagnose_if)
+# define RBIMPL_ATTR_DIAGNOSE_IF(_, __, ___) \
+ RBIMPL_WARNING_PUSH() \
+ RBIMPL_WARNING_IGNORED(-Wgcc-compat) \
+ __attribute__((__diagnose_if__(_, __, ___))) \
+ RBIMPL_WARNING_POP()
+
+#else
+# define RBIMPL_ATTR_DIAGNOSE_IF(_, __, ___) /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_DIAGNOSE_IF_H */
diff --git a/include/ruby/internal/attr/enum_extensibility.h b/include/ruby/internal/attr/enum_extensibility.h
new file mode 100644
index 0000000000..eb0d5b6e9b
--- /dev/null
+++ b/include/ruby/internal/attr/enum_extensibility.h
@@ -0,0 +1,32 @@
+#ifndef RBIMPL_ATTR_ENUM_EXTENSIBILITY_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_ENUM_EXTENSIBILITY_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief #RBIMPL_ATTR_ENUM_EXTENSIBILITY.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((enum_extensibility))` */
+#if RBIMPL_HAS_ATTRIBUTE(enum_extensibility)
+# define RBIMPL_ATTR_ENUM_EXTENSIBILITY(_) __attribute__((__enum_extensibility__(_)))
+#else
+# define RBIMPL_ATTR_ENUM_EXTENSIBILITY(_) /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_ENUM_EXTENSIBILITY_H */
diff --git a/include/ruby/internal/attr/error.h b/include/ruby/internal/attr/error.h
new file mode 100644
index 0000000000..2ed388a770
--- /dev/null
+++ b/include/ruby/internal/attr/error.h
@@ -0,0 +1,32 @@
+#ifndef RBIMPL_ATTR_ERROR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_ERROR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_ERROR.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((error))` */
+#if RBIMPL_HAS_ATTRIBUTE(error)
+# define RBIMPL_ATTR_ERROR(msg) __attribute__((__error__ msg))
+#else
+# define RBIMPL_ATTR_ERROR(msg) /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_ERROR_H */
diff --git a/include/ruby/internal/attr/flag_enum.h b/include/ruby/internal/attr/flag_enum.h
new file mode 100644
index 0000000000..3053d75074
--- /dev/null
+++ b/include/ruby/internal/attr/flag_enum.h
@@ -0,0 +1,33 @@
+#ifndef RBIMPL_ATTR_FLAG_ENUM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_FLAG_ENUM_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_FLAG_ENUM.
+ * @see https://clang.llvm.org/docs/AttributeReference.html#flag_enum
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((flag_enum)` */
+#if RBIMPL_HAS_ATTRIBUTE(flag_enum)
+# define RBIMPL_ATTR_FLAG_ENUM() __attribute__((__flag_enum__))
+#else
+# define RBIMPL_ATTR_FLAG_ENUM() /* void */
+#endif
+
+#endif /* RBIMPLATTR_FLAG_ENUM_H */
diff --git a/include/ruby/internal/attr/forceinline.h b/include/ruby/internal/attr/forceinline.h
new file mode 100644
index 0000000000..5b9ae794af
--- /dev/null
+++ b/include/ruby/internal/attr/forceinline.h
@@ -0,0 +1,40 @@
+#ifndef RBIMPL_ATTR_FORCEINLINE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_FORCEINLINE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_FORCEINLINE.
+ */
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/attribute.h"
+
+/**
+ * Wraps (or simulates) `__forceinline`. MSVC complains on declarations like
+ * `static inline __forceinline void foo()`. It seems MSVC's `inline` and
+ * `__forceinline` are mutually exclusive. We have to mimic that behaviour for
+ * non-MSVC compilers.
+ */
+#if RBIMPL_COMPILER_IS(MSVC)
+# define RBIMPL_ATTR_FORCEINLINE() __forceinline
+#elif RBIMPL_HAS_ATTRIBUTE(always_inline)
+# define RBIMPL_ATTR_FORCEINLINE() __attribute__((__always_inline__)) inline
+#else
+# define RBIMPL_ATTR_FORCEINLINE() inline
+#endif
+
+#endif /* RBIMPL_ATTR_FORCEINLINE_H */
diff --git a/include/ruby/internal/attr/format.h b/include/ruby/internal/attr/format.h
new file mode 100644
index 0000000000..b3488ee00a
--- /dev/null
+++ b/include/ruby/internal/attr/format.h
@@ -0,0 +1,38 @@
+#ifndef RBIMPL_ATTR_FORMAT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_FORMAT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_FORMAT.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((format))` */
+#if RBIMPL_HAS_ATTRIBUTE(format)
+# define RBIMPL_ATTR_FORMAT(x, y, z) __attribute__((__format__(x, y, z)))
+#else
+# define RBIMPL_ATTR_FORMAT(x, y, z) /* void */
+#endif
+
+#if defined(__MINGW_PRINTF_FORMAT)
+# define RBIMPL_PRINTF_FORMAT __MINGW_PRINTF_FORMAT
+#else
+# define RBIMPL_PRINTF_FORMAT __printf__
+#endif
+
+#endif /* RBIMPL_ATTR_FORMAT_H */
diff --git a/include/ruby/internal/attr/maybe_unused.h b/include/ruby/internal/attr/maybe_unused.h
new file mode 100644
index 0000000000..3ee8be4540
--- /dev/null
+++ b/include/ruby/internal/attr/maybe_unused.h
@@ -0,0 +1,38 @@
+#ifndef RBIMPL_ATTR_MAYBE_UNUSED_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_MAYBE_UNUSED_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_MAYBE_UNUSED.
+ */
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/has/c_attribute.h"
+#include "ruby/internal/has/cpp_attribute.h"
+
+/** Wraps (or simulates) `[[maybe_unused]]` */
+#if RBIMPL_HAS_CPP_ATTRIBUTE(maybe_unused)
+# define RBIMPL_ATTR_MAYBE_UNUSED() [[maybe_unused]]
+#elif RBIMPL_HAS_C_ATTRIBUTE(maybe_unused)
+# define RBIMPL_ATTR_MAYBE_UNUSED() [[maybe_unused]]
+#elif RBIMPL_HAS_ATTRIBUTE(unused)
+# define RBIMPL_ATTR_MAYBE_UNUSED() __attribute__((__unused__))
+#else
+# define RBIMPL_ATTR_MAYBE_UNUSED() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_MAYBE_UNUSED */
diff --git a/include/ruby/internal/attr/noalias.h b/include/ruby/internal/attr/noalias.h
new file mode 100644
index 0000000000..0790ef60e5
--- /dev/null
+++ b/include/ruby/internal/attr/noalias.h
@@ -0,0 +1,69 @@
+#ifndef RBIMPL_ATTR_NOALIAS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NOALIAS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NOALIAS.
+ *
+ * ### Q&A ###
+ *
+ * - Q: There are seemingly similar attributes named #RBIMPL_ATTR_CONST,
+ * #RBIMPL_ATTR_PURE, and #RBIMPL_ATTR_NOALIAS. What are the difference?
+ *
+ * - A: Allowed operations are different.
+ *
+ * - #RBIMPL_ATTR_CONST ... Functions attributed by this are not allowed to
+ * read/write _any_ pointers at all (there are exceptional situations
+ * when reading a pointer is possible but forget that; they are too
+ * exceptional to be useful). Just remember that everything pointer-
+ * related are NG.
+ *
+ * - #RBIMPL_ATTR_PURE ... Functions attributed by this can read any
+ * nonvolatile pointers, but no writes are allowed at all. The ability
+ * to read _any_ nonvolatile pointers makes it possible to mark ::VALUE-
+ * taking functions as being pure, as long as they are read-only.
+ *
+ * - #RBIMPL_ATTR_NOALIAS ... Can both read/write, but only through
+ * pointers passed to the function as parameters. This is a typical
+ * situation when you create a C++ non-static member function which only
+ * concerns `this`. No global variables are allowed to read/write. So
+ * this is not a super-set of being pure. If you want to read something,
+ * that has to be passed to the function as a pointer. ::VALUE -taking
+ * functions thus cannot be attributed as such.
+ */
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/declspec_attribute.h"
+
+/** Wraps (or simulates) `__declspec((noalias))` */
+#if RBIMPL_COMPILER_BEFORE(Clang, 12, 0, 0)
+# /*
+# * `::llvm::Attribute::ArgMemOnly` was buggy before. Maybe because nobody
+# * actually seriously used it. It seems they somehow mitigated the situation
+# * in LLVM 12. Still not found the exact changeset which fiexed the
+# * attribute, though.
+# *
+# * :FIXME: others (armclang, xlclang, ...) can also be affected?
+# */
+# define RBIMPL_ATTR_NOALIAS() /* void */
+#elif RBIMPL_HAS_DECLSPEC_ATTRIBUTE(noalias)
+# define RBIMPL_ATTR_NOALIAS() __declspec(noalias)
+#else
+# define RBIMPL_ATTR_NOALIAS() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NOALIAS_H */
diff --git a/include/ruby/internal/attr/nodiscard.h b/include/ruby/internal/attr/nodiscard.h
new file mode 100644
index 0000000000..c3ae118942
--- /dev/null
+++ b/include/ruby/internal/attr/nodiscard.h
@@ -0,0 +1,45 @@
+#ifndef RBIMPL_ATTR_NODISCARD_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NODISCARD_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NODISCARD.
+ */
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/has/c_attribute.h"
+#include "ruby/internal/has/cpp_attribute.h"
+
+/**
+ * Wraps (or simulates) `[[nodiscard]]`. In C++ (at least since C++20) a
+ * nodiscard attribute can have a message why the result shall not be ignored.
+ * However GCC attribute and SAL annotation cannot take them.
+ */
+#if RBIMPL_HAS_CPP_ATTRIBUTE(nodiscard)
+# define RBIMPL_ATTR_NODISCARD() [[nodiscard]]
+#elif RBIMPL_HAS_C_ATTRIBUTE(nodiscard)
+# define RBIMPL_ATTR_NODISCARD() [[nodiscard]]
+#elif RBIMPL_HAS_ATTRIBUTE(warn_unused_result)
+# define RBIMPL_ATTR_NODISCARD() __attribute__((__warn_unused_result__))
+#elif defined(_Check_return_)
+# /* Take SAL definition. */
+# define RBIMPL_ATTR_NODISCARD() _Check_return_
+#else
+# define RBIMPL_ATTR_NODISCARD() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NODISCARD_H */
diff --git a/include/ruby/internal/attr/noexcept.h b/include/ruby/internal/attr/noexcept.h
new file mode 100644
index 0000000000..dd4c667407
--- /dev/null
+++ b/include/ruby/internal/attr/noexcept.h
@@ -0,0 +1,91 @@
+#ifndef RBIMPL_ATTR_NOEXCEPT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NOEXCEPT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NOEXCEPT.
+ *
+ * This isn't actually an attribute in C++ but who cares...
+ *
+ * Mainly due to aesthetic reasons, this one is rarely used in the project.
+ * But can be handy on occasions, especially when a function's noexcept-ness
+ * depends on its calling functions.
+ *
+ * ### Q&A ###
+ *
+ * - Q: Can a function that raises Ruby exceptions be attributed `noexcept`?
+ *
+ * - A: Yes. `noexcept` is about C++ exceptions, not Ruby's. They don't
+ * interface each other. You can safely attribute a function that raises
+ * Ruby exceptions as `noexcept`.
+ *
+ * - Q: How, then, can I assert that a function I wrote doesn't raise any Ruby
+ * exceptions?
+ *
+ * - A: `__attribute__((__leaf__))` is for that purpose. A function attributed
+ * as leaf can still throw C++ exceptions, but not Ruby's. Note however,
+ * that it's extremely difficult -- if not impossible -- to assert that a
+ * function doesn't raise any Ruby exceptions at all. Use of that
+ * attribute is not recommended; mere mortals can't properly use that by
+ * hand.
+ *
+ * - Q: Does it make sense to attribute an inline function `noexcept`?
+ *
+ * - A: I thought so before. But no, I don't think they are useful any longer.
+ *
+ * - When an inline function attributed `noexcept` actually doesn't throw
+ * any exceptions at all: these days I don't see any difference in
+ * generated assembly by adding/removing this attribute. C++ compilers
+ * get smarter and smarter. Today they can infer if it actually throws
+ * or not without any annotations by humans (correct me if I'm wrong).
+ *
+ * - When an inline function attributed `noexcept` actually _does_ throw an
+ * exception: they have to call `std::terminate` then (C++ standard
+ * mandates so). This means exception handling routines are actually
+ * enforced, not omitted. This doesn't impact runtime performance (The
+ * Itanium C++ ABI has zero-cost exception handling), but does impact on
+ * generated binary size. This is bad.
+ */
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/feature.h"
+
+/** Wraps (or simulates) C++11 `noexcept` */
+#if ! defined(__cplusplus)
+# /* Doesn't make sense. */
+# define RBIMPL_ATTR_NOEXCEPT(_) /* void */
+
+#elif RBIMPL_HAS_FEATURE(cxx_noexcept)
+# define RBIMPL_ATTR_NOEXCEPT(_) noexcept(noexcept(_))
+
+#elif defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__
+# define RBIMPL_ATTR_NOEXCEPT(_) noexcept(noexcept(_))
+
+#elif defined(__INTEL_CXX11_MODE__)
+# define RBIMPL_ATTR_NOEXCEPT(_) noexcept(noexcept(_))
+
+#elif RBIMPL_COMPILER_IS(MSVC)
+# define RBIMPL_ATTR_NOEXCEPT(_) noexcept(noexcept(_))
+
+#elif __cplusplus >= 201103L
+# define RBIMPL_ATTR_NOEXCEPT(_) noexcept(noexcept(_))
+
+#else
+# define RBIMPL_ATTR_NOEXCEPT(_) /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NOEXCEPT_H */
diff --git a/include/ruby/internal/attr/noinline.h b/include/ruby/internal/attr/noinline.h
new file mode 100644
index 0000000000..b7605a0c91
--- /dev/null
+++ b/include/ruby/internal/attr/noinline.h
@@ -0,0 +1,35 @@
+#ifndef RBIMPL_ATTR_NOINLINE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NOINLINE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NOINLINE.
+ */
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/has/declspec_attribute.h"
+
+/** Wraps (or simulates) `__declspec(noinline)` */
+#if RBIMPL_HAS_DECLSPEC_ATTRIBUTE(noinline)
+# define RBIMPL_ATTR_NOINLINE() __declspec(noinline)
+#elif RBIMPL_HAS_ATTRIBUTE(noinline)
+# define RBIMPL_ATTR_NOINLINE() __attribute__((__noinline__))
+#else
+# define RBIMPL_ATTR_NOINLINE() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NOINLINE_H */
diff --git a/include/ruby/internal/attr/nonnull.h b/include/ruby/internal/attr/nonnull.h
new file mode 100644
index 0000000000..778d5be208
--- /dev/null
+++ b/include/ruby/internal/attr/nonnull.h
@@ -0,0 +1,34 @@
+#ifndef RBIMPL_ATTR_NONNULL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NONNULL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NONNULL.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((nonnull))` */
+#if RBIMPL_HAS_ATTRIBUTE(nonnull)
+# define RBIMPL_ATTR_NONNULL(list) __attribute__((__nonnull__ list))
+# define RBIMPL_NONNULL_ARG(arg) RBIMPL_ASSERT_NOTHING
+#else
+# define RBIMPL_ATTR_NONNULL(list) /* void */
+# define RBIMPL_NONNULL_ARG(arg) RUBY_ASSERT(arg)
+#endif
+
+#endif /* RBIMPL_ATTR_NONNULL_H */
diff --git a/include/ruby/internal/attr/nonstring.h b/include/ruby/internal/attr/nonstring.h
new file mode 100644
index 0000000000..5ad6ef2a86
--- /dev/null
+++ b/include/ruby/internal/attr/nonstring.h
@@ -0,0 +1,40 @@
+#ifndef RBIMPL_ATTR_NONSTRING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NONSTRING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NONSTRING.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((nonstring))` */
+#if RBIMPL_HAS_ATTRIBUTE(nonstring)
+# define RBIMPL_ATTR_NONSTRING() __attribute__((nonstring))
+# if RBIMPL_COMPILER_SINCE(GCC, 15, 0, 0)
+# define RBIMPL_ATTR_NONSTRING_ARRAY() RBIMPL_ATTR_NONSTRING()
+# elif RBIMPL_COMPILER_SINCE(Clang, 21, 0, 0)
+# define RBIMPL_ATTR_NONSTRING_ARRAY() RBIMPL_ATTR_NONSTRING()
+# else
+# define RBIMPL_ATTR_NONSTRING_ARRAY() /* void */
+# endif
+#else
+# define RBIMPL_ATTR_NONSTRING() /* void */
+# define RBIMPL_ATTR_NONSTRING_ARRAY() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NONSTRING_H */
diff --git a/include/ruby/internal/attr/noreturn.h b/include/ruby/internal/attr/noreturn.h
new file mode 100644
index 0000000000..5839212037
--- /dev/null
+++ b/include/ruby/internal/attr/noreturn.h
@@ -0,0 +1,48 @@
+#ifndef RBIMPL_ATTR_NORETURN_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_NORETURN_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_NORETURN.
+ */
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/has/cpp_attribute.h"
+#include "ruby/internal/has/declspec_attribute.h"
+
+/** Wraps (or simulates) `[[noreturn]]` */
+#if RBIMPL_HAS_DECLSPEC_ATTRIBUTE(noreturn)
+# define RBIMPL_ATTR_NORETURN() __declspec(noreturn)
+
+#elif RBIMPL_HAS_ATTRIBUTE(noreturn)
+# define RBIMPL_ATTR_NORETURN() __attribute__((__noreturn__))
+
+#elif RBIMPL_HAS_CPP_ATTRIBUTE(noreturn)
+# define RBIMPL_ATTR_NORETURN() [[noreturn]]
+
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112)
+# define RBIMPL_ATTR_NORETURN() _Noreturn
+
+#elif defined(_Noreturn)
+# /* glibc <sys/cdefs.h> has this macro. */
+# define RBIMPL_ATTR_NORETURN() _Noreturn
+
+#else
+# define RBIMPL_ATTR_NORETURN() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_NORETURN_H */
diff --git a/include/ruby/internal/attr/packed_struct.h b/include/ruby/internal/attr/packed_struct.h
new file mode 100644
index 0000000000..0678b9acc8
--- /dev/null
+++ b/include/ruby/internal/attr/packed_struct.h
@@ -0,0 +1,43 @@
+#ifndef RBIMPL_ATTR_PACKED_STRUCT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_PACKED_STRUCT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_PACKED_STRUCT_BEGIN,
+ * #RBIMPL_ATTR_PACKED_STRUCT_END,
+ * #RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN, and
+ * #RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END.
+ */
+#include "ruby/internal/config.h"
+
+#ifndef RBIMPL_ATTR_PACKED_STRUCT_BEGIN
+# define RBIMPL_ATTR_PACKED_STRUCT_BEGIN() /* void */
+#endif
+#ifndef RBIMPL_ATTR_PACKED_STRUCT_END
+# define RBIMPL_ATTR_PACKED_STRUCT_END() /* void */
+#endif
+
+#if UNALIGNED_WORD_ACCESS
+# define RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() RBIMPL_ATTR_PACKED_STRUCT_BEGIN()
+# define RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END() RBIMPL_ATTR_PACKED_STRUCT_END()
+#else
+# define RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN() /* void */
+# define RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END() /* void */
+#endif
+
+#endif
diff --git a/include/ruby/internal/attr/pure.h b/include/ruby/internal/attr/pure.h
new file mode 100644
index 0000000000..015711bdab
--- /dev/null
+++ b/include/ruby/internal/attr/pure.h
@@ -0,0 +1,43 @@
+#ifndef RBIMPL_ATTR_PURE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_PURE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_PURE.
+ */
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/attribute.h"
+#include "ruby/assert.h"
+
+/** Wraps (or simulates) `__attribute__((pure))` */
+#if RBIMPL_HAS_ATTRIBUTE(pure)
+# define RBIMPL_ATTR_PURE() __attribute__((__pure__))
+#elif RBIMPL_COMPILER_SINCE(SunPro, 5, 10, 0)
+# define RBIMPL_ATTR_PURE() _Pragma("does_not_write_global_data")
+#else
+# define RBIMPL_ATTR_PURE() /* void */
+#endif
+
+/** Enables #RBIMPL_ATTR_PURE if and only if. ! #RUBY_DEBUG. */
+#if !RUBY_DEBUG
+# define RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_PURE()
+#else
+# define RBIMPL_ATTR_PURE_UNLESS_DEBUG() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_PURE_H */
diff --git a/include/ruby/internal/attr/restrict.h b/include/ruby/internal/attr/restrict.h
new file mode 100644
index 0000000000..b12fdc9dbc
--- /dev/null
+++ b/include/ruby/internal/attr/restrict.h
@@ -0,0 +1,44 @@
+#ifndef RBIMPL_ATTR_RESTRICT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_RESTRICT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_RESTRICT.
+ */
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/attribute.h"
+
+/* :FIXME: config.h includes conflicting `#define restrict`. MSVC can be
+ * detected using `RBIMPL_COMPILER_SINCE()`, but Clang & family cannot use
+ * `__has_declspec_attribute()` which involves macro substitution. */
+
+/** Wraps (or simulates) `__declspec(restrict)` */
+#if RBIMPL_COMPILER_IS(MSVC)
+# define RBIMPL_ATTR_RESTRICT() __declspec(re ## strict)
+
+#elif RBIMPL_HAS_ATTRIBUTE(malloc)
+# define RBIMPL_ATTR_RESTRICT() __attribute__((__malloc__))
+
+#elif RBIMPL_COMPILER_SINCE(SunPro, 5, 10, 0)
+# define RBIMPL_ATTR_RESTRICT() _Pragma("returns_new_memory")
+
+#else
+# define RBIMPL_ATTR_RESTRICT() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_RESTRICT_H */
diff --git a/include/ruby/internal/attr/returns_nonnull.h b/include/ruby/internal/attr/returns_nonnull.h
new file mode 100644
index 0000000000..5d6f1d1459
--- /dev/null
+++ b/include/ruby/internal/attr/returns_nonnull.h
@@ -0,0 +1,37 @@
+#ifndef RBIMPL_ATTR_RETURNS_NONNULL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_RETURNS_NONNULL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_RETURNS_NONNULL.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((returns_nonnull))` */
+#if defined(_Ret_nonnull_)
+# /* Take SAL definition. */
+# define RBIMPL_ATTR_RETURNS_NONNULL() _Ret_nonnull_
+
+#elif RBIMPL_HAS_ATTRIBUTE(returns_nonnull)
+# define RBIMPL_ATTR_RETURNS_NONNULL() __attribute__((__returns_nonnull__))
+
+#else
+# define RBIMPL_ATTR_RETURNS_NONNULL() /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_RETURNS_NONNULL_H */
diff --git a/include/ruby/internal/attr/warning.h b/include/ruby/internal/attr/warning.h
new file mode 100644
index 0000000000..e5ced269b8
--- /dev/null
+++ b/include/ruby/internal/attr/warning.h
@@ -0,0 +1,32 @@
+#ifndef RBIMPL_ATTR_WARNING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_WARNING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_WARNING.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((warning))` */
+#if RBIMPL_HAS_ATTRIBUTE(warning)
+# define RBIMPL_ATTR_WARNING(msg) __attribute__((__warning__ msg))
+#else
+# define RBIMPL_ATTR_WARNING(msg) /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_WARNING_H */
diff --git a/include/ruby/internal/attr/weakref.h b/include/ruby/internal/attr/weakref.h
new file mode 100644
index 0000000000..f118bb62b8
--- /dev/null
+++ b/include/ruby/internal/attr/weakref.h
@@ -0,0 +1,32 @@
+#ifndef RBIMPL_ATTR_WEAKREF_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ATTR_WEAKREF_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ATTR_WEAKREF.
+ */
+#include "ruby/internal/has/attribute.h"
+
+/** Wraps (or simulates) `__attribute__((weakref))` */
+#if RBIMPL_HAS_ATTRIBUTE(weakref)
+# define RBIMPL_ATTR_WEAKREF(sym) __attribute__((__weakref__(# sym)))
+#else
+# define RBIMPL_ATTR_WEAKREF(sym) /* void */
+#endif
+
+#endif /* RBIMPL_ATTR_WEAKREF_H */
diff --git a/include/ruby/internal/cast.h b/include/ruby/internal/cast.h
new file mode 100644
index 0000000000..a31fddbe4c
--- /dev/null
+++ b/include/ruby/internal/cast.h
@@ -0,0 +1,50 @@
+#ifndef RBIMPL_CAST_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_CAST_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines RBIMPL_CAST.
+ *
+ * This casting macro makes sense only inside of other macros that are part of
+ * public headers. They could be used from C++, and C-style casts could issue
+ * warnings. Ruby internals are pure C so they should not bother.
+ */
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/has/warning.h"
+#include "ruby/internal/warning_push.h"
+
+#if ! defined(__cplusplus)
+# define RBIMPL_CAST(expr) (expr)
+
+#elif RBIMPL_COMPILER_SINCE(GCC, 4, 6, 0)
+# /* g++ has -Wold-style-cast since 1997 or so, but its _Pragma is broken. */
+# /* See https://gcc.godbolt.org/z/XWhU6J */
+# define RBIMPL_CAST(expr) (expr)
+# pragma GCC diagnostic ignored "-Wold-style-cast"
+
+#elif RBIMPL_HAS_WARNING("-Wold-style-cast")
+# define RBIMPL_CAST(expr) \
+ RBIMPL_WARNING_PUSH() \
+ RBIMPL_WARNING_IGNORED(-Wold-style-cast) \
+ (expr) \
+ RBIMPL_WARNING_POP()
+
+#else
+# define RBIMPL_CAST(expr) (expr)
+#endif
+
+#endif /* RBIMPL_CAST_H */
diff --git a/include/ruby/internal/compiler_is.h b/include/ruby/internal/compiler_is.h
new file mode 100644
index 0000000000..7070b033a0
--- /dev/null
+++ b/include/ruby/internal/compiler_is.h
@@ -0,0 +1,45 @@
+#ifndef RBIMPL_COMPILER_IS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_COMPILER_IS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_COMPILER_IS.
+ */
+
+/**
+ * @brief Checks if the compiler is of given brand.
+ * @param cc Compiler brand, like `MSVC`.
+ * @retval true It is.
+ * @retval false It isn't.
+ */
+#define RBIMPL_COMPILER_IS(cc) RBIMPL_COMPILER_IS_ ## cc
+
+#include "ruby/internal/compiler_is/apple.h"
+#include "ruby/internal/compiler_is/clang.h"
+#include "ruby/internal/compiler_is/gcc.h"
+#include "ruby/internal/compiler_is/intel.h"
+#include "ruby/internal/compiler_is/msvc.h"
+#include "ruby/internal/compiler_is/sunpro.h"
+/* :TODO: Other possible compilers to support:
+ *
+ * - IBM XL: recent XL are clang-backended so some tweaks like we do for
+ * Apple's might be needed.
+ *
+ * - ARM's armclang: ditto, it can be clang-backended. */
+
+#endif /* RBIMPL_COMPILER_IS_H */
diff --git a/include/ruby/internal/compiler_is/apple.h b/include/ruby/internal/compiler_is/apple.h
new file mode 100644
index 0000000000..a81f1f2c8f
--- /dev/null
+++ b/include/ruby/internal/compiler_is/apple.h
@@ -0,0 +1,40 @@
+#ifndef RBIMPL_COMPILER_IS_APPLE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_COMPILER_IS_APPLE_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines RBIMPL_COMPILER_IS_Apple.
+ *
+ * Apple ships clang. Problem is, its `__clang_major__` etc. are not the
+ * upstream LLVM version, but XCode's. We have to think Apple's is distinct
+ * from LLVM's, when it comes to compiler detection business in this header
+ * file.
+ */
+#if ! defined(__clang__)
+# define RBIMPL_COMPILER_IS_Apple 0
+
+#elif ! defined(__apple_build_version__)
+# define RBIMPL_COMPILER_IS_Apple 0
+
+#else
+# define RBIMPL_COMPILER_IS_Apple 1
+# define RBIMPL_COMPILER_VERSION_MAJOR __clang_major__
+# define RBIMPL_COMPILER_VERSION_MINOR __clang_minor__
+# define RBIMPL_COMPILER_VERSION_PATCH __clang_patchlevel__
+#endif
+
+#endif /* RBIMPL_COMPILER_IS_APPLE_H */
diff --git a/include/ruby/internal/compiler_is/clang.h b/include/ruby/internal/compiler_is/clang.h
new file mode 100644
index 0000000000..169ff789f6
--- /dev/null
+++ b/include/ruby/internal/compiler_is/clang.h
@@ -0,0 +1,37 @@
+#ifndef RBIMPL_COMPILER_IS_CLANG_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_COMPILER_IS_CLANG_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines RBIMPL_COMPILER_IS_Clang.
+ */
+#include "ruby/internal/compiler_is/apple.h"
+
+#if ! defined(__clang__)
+# define RBIMPL_COMPILER_IS_Clang 0
+
+#elif RBIMPL_COMPILER_IS(Apple)
+# define RBIMPL_COMPILER_IS_Clang 0
+
+#else
+# define RBIMPL_COMPILER_IS_Clang 1
+# define RBIMPL_COMPILER_VERSION_MAJOR __clang_major__
+# define RBIMPL_COMPILER_VERSION_MINOR __clang_minor__
+# define RBIMPL_COMPILER_VERSION_PATCH __clang_patchlevel__
+#endif
+
+#endif /* RBIMPL_COMPILER_IS_CLANG_H */
diff --git a/include/ruby/internal/compiler_is/gcc.h b/include/ruby/internal/compiler_is/gcc.h
new file mode 100644
index 0000000000..accc80e9aa
--- /dev/null
+++ b/include/ruby/internal/compiler_is/gcc.h
@@ -0,0 +1,45 @@
+#ifndef RBIMPL_COMPILER_IS_GCC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_COMPILER_IS_GCC_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines RBIMPL_COMPILER_IS_GCC.
+ */
+#include "ruby/internal/compiler_is/apple.h"
+#include "ruby/internal/compiler_is/clang.h"
+#include "ruby/internal/compiler_is/intel.h"
+
+#if ! defined(__GNUC__)
+# define RBIMPL_COMPILER_IS_GCC 0
+
+#elif RBIMPL_COMPILER_IS(Apple)
+# define RBIMPL_COMPILER_IS_GCC 0
+
+#elif RBIMPL_COMPILER_IS(Clang)
+# define RBIMPL_COMPILER_IS_GCC 0
+
+#elif RBIMPL_COMPILER_IS(Intel)
+# define RBIMPL_COMPILER_IS_GCC 0
+
+#else
+# define RBIMPL_COMPILER_IS_GCC 1
+# define RBIMPL_COMPILER_VERSION_MAJOR __GNUC__
+# define RBIMPL_COMPILER_VERSION_MINOR __GNUC_MINOR__
+# define RBIMPL_COMPILER_VERSION_PATCH __GNUC_PATCHLEVEL__
+#endif
+
+#endif /* RBIMPL_COMPILER_IS_GCC_H */
diff --git a/include/ruby/internal/compiler_is/intel.h b/include/ruby/internal/compiler_is/intel.h
new file mode 100644
index 0000000000..377946ace0
--- /dev/null
+++ b/include/ruby/internal/compiler_is/intel.h
@@ -0,0 +1,40 @@
+#ifndef RBIMPL_COMPILER_IS_INTEL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_COMPILER_IS_INTEL_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines RBIMPL_COMPILER_IS_Intel.
+ */
+#if ! defined(__INTEL_COMPILER)
+# define RBIMPL_COMPILER_IS_Intel 0
+
+#elif ! defined(__INTEL_COMPILER_UPDATE)
+# define RBIMPL_COMPILER_IS_Intel 1
+# /* __INTEL_COMPILER = XXYZ */
+# define RBIMPL_COMPILER_VERSION_MAJOR (__INTEL_COMPILER / 100)
+# define RBIMPL_COMPILER_VERSION_MINOR (__INTEL_COMPILER % 100 / 10)
+# define RBIMPL_COMPILER_VERSION_PATCH (__INTEL_COMPILER % 10)
+
+#else
+# define RBIMPL_COMPILER_IS_Intel 1
+# /* __INTEL_COMPILER = XXYZ */
+# define RBIMPL_COMPILER_VERSION_MAJOR (__INTEL_COMPILER / 100)
+# define RBIMPL_COMPILER_VERSION_MINOR (__INTEL_COMPILER % 100 / 10)
+# define RBIMPL_COMPILER_VERSION_PATCH __INTEL_COMPILER_UPDATE
+#endif
+
+#endif /* RBIMPL_COMPILER_IS_INTEL_H */
diff --git a/include/ruby/internal/compiler_is/msvc.h b/include/ruby/internal/compiler_is/msvc.h
new file mode 100644
index 0000000000..824f0ecc21
--- /dev/null
+++ b/include/ruby/internal/compiler_is/msvc.h
@@ -0,0 +1,45 @@
+#ifndef RBIMPL_COMPILER_IS_MSVC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_COMPILER_IS_MSVC_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines RBIMPL_COMPILER_IS_MSVC.
+ */
+#include "ruby/internal/compiler_is/clang.h"
+#include "ruby/internal/compiler_is/intel.h"
+
+#if ! defined(_MSC_VER)
+# define RBIMPL_COMPILER_IS_MSVC 0
+
+#elif RBIMPL_COMPILER_IS(Clang)
+# define RBIMPL_COMPILER_IS_MSVC 0
+
+#elif RBIMPL_COMPILER_IS(Intel)
+# define RBIMPL_COMPILER_IS_MSVC 0
+
+#elif _MSC_VER >= 1400
+# define RBIMPL_COMPILER_IS_MSVC 1
+# /* _MSC_FULL_VER = XXYYZZZZZ */
+# define RBIMPL_COMPILER_VERSION_MAJOR (_MSC_FULL_VER / 10000000)
+# define RBIMPL_COMPILER_VERSION_MINOR (_MSC_FULL_VER % 10000000 / 100000)
+# define RBIMPL_COMPILER_VERSION_PATCH (_MSC_FULL_VER % 100000)
+
+#else
+# error Unsupported MSVC version
+#endif
+
+#endif /* RBIMPL_COMPILER_IS_MSVC_H */
diff --git a/include/ruby/internal/compiler_is/sunpro.h b/include/ruby/internal/compiler_is/sunpro.h
new file mode 100644
index 0000000000..c11c8452e7
--- /dev/null
+++ b/include/ruby/internal/compiler_is/sunpro.h
@@ -0,0 +1,54 @@
+#ifndef RBIMPL_COMPILER_IS_SUNPRO_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_COMPILER_IS_SUNPRO_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines RBIMPL_COMPILER_IS_SunPro.
+ */
+#if ! (defined(__SUNPRO_C) || defined(__SUNPRO_CC))
+# define RBIMPL_COMPILER_IS_SunPro 0
+
+#elif defined(__SUNPRO_C) && __SUNPRO_C >= 0x5100
+# define RBIMPL_COMPILER_IS_SunPro 1
+# /* __SUNPRO_C = 0xXYYZ */
+# define RBIMPL_COMPILER_VERSION_MAJOR (__SUNPRO_C >> 12)
+# define RBIMPL_COMPILER_VERSION_MINOR ((__SUNPRO_C >> 8 & 0xF) * 10 + (__SUNPRO_C >> 4 & 0xF))
+# define RBIMPL_COMPILER_VERSION_PATCH (__SUNPRO_C & 0xF)
+
+#elif defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5100
+# define RBIMPL_COMPILER_IS_SunPro 1
+# /* __SUNPRO_CC = 0xXYYZ */
+# define RBIMPL_COMPILER_VERSION_MAJOR (__SUNPRO_CC >> 12)
+# define RBIMPL_COMPILER_VERSION_MINOR ((__SUNPRO_CC >> 8 & 0xF) * 10 + (__SUNPRO_CC >> 4 & 0xF))
+# define RBIMPL_COMPILER_VERSION_PATCH (__SUNPRO_CC & 0xF)
+
+#elif defined(__SUNPRO_C)
+# define RBIMPL_COMPILER_IS_SunPro 1
+# /* __SUNPRO_C = 0xXYZ */
+# define RBIMPL_COMPILER_VERSION_MAJOR (__SUNPRO_C >> 8)
+# define RBIMPL_COMPILER_VERSION_MINOR (__SUNPRO_C >> 4 & 0xF)
+# define RBIMPL_COMPILER_VERSION_PATCH (__SUNPRO_C & 0xF)
+
+#else
+# define RBIMPL_COMPILER_IS_SunPro 1
+# /* __SUNPRO_CC = 0xXYZ */
+# define RBIMPL_COMPILER_VERSION_MAJOR (__SUNPRO_CC >> 8)
+# define RBIMPL_COMPILER_VERSION_MINOR (__SUNPRO_CC >> 4 & 0xF)
+# define RBIMPL_COMPILER_VERSION_PATCH (__SUNPRO_CC & 0xF)
+#endif
+
+#endif /* RBIMPL_COMPILER_IS_SUNPRO_H */
diff --git a/include/ruby/internal/compiler_since.h b/include/ruby/internal/compiler_since.h
new file mode 100644
index 0000000000..1929032884
--- /dev/null
+++ b/include/ruby/internal/compiler_since.h
@@ -0,0 +1,61 @@
+#ifndef RBIMPL_COMPILER_SINCE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_COMPILER_SINCE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_COMPILER_SINCE.
+ */
+#include "ruby/internal/compiler_is.h"
+
+/**
+ * @brief Checks if the compiler is of given brand and is newer than or equal
+ * to the passed version.
+ * @param cc Compiler brand, like `MSVC`.
+ * @param x Major version.
+ * @param y Minor version.
+ * @param z Patchlevel.
+ * @retval true cc >= x.y.z.
+ * @retval false otherwise.
+ */
+#define RBIMPL_COMPILER_SINCE(cc, x, y, z) \
+ (RBIMPL_COMPILER_IS(cc) && \
+ ((RBIMPL_COMPILER_VERSION_MAJOR > (x)) || \
+ ((RBIMPL_COMPILER_VERSION_MAJOR == (x)) && \
+ ((RBIMPL_COMPILER_VERSION_MINOR > (y)) || \
+ ((RBIMPL_COMPILER_VERSION_MINOR == (y)) && \
+ (RBIMPL_COMPILER_VERSION_PATCH >= (z)))))))
+
+/**
+ * @brief Checks if the compiler is of given brand and is older than the
+ * passed version.
+ * @param cc Compiler brand, like `MSVC`.
+ * @param x Major version.
+ * @param y Minor version.
+ * @param z Patchlevel.
+ * @retval true cc < x.y.z.
+ * @retval false otherwise.
+ */
+#define RBIMPL_COMPILER_BEFORE(cc, x, y, z) \
+ (RBIMPL_COMPILER_IS(cc) && \
+ ((RBIMPL_COMPILER_VERSION_MAJOR < (x)) || \
+ ((RBIMPL_COMPILER_VERSION_MAJOR == (x)) && \
+ ((RBIMPL_COMPILER_VERSION_MINOR < (y)) || \
+ ((RBIMPL_COMPILER_VERSION_MINOR == (y)) && \
+ (RBIMPL_COMPILER_VERSION_PATCH < (z)))))))
+
+#endif /* RBIMPL_COMPILER_SINCE_H */
diff --git a/include/ruby/internal/config.h b/include/ruby/internal/config.h
new file mode 100644
index 0000000000..34862ded6e
--- /dev/null
+++ b/include/ruby/internal/config.h
@@ -0,0 +1,151 @@
+#ifndef RBIMPL_CONFIG_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_CONFIG_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Thin wrapper to ruby/config.h
+ */
+#include "ruby/config.h"
+
+#ifdef RUBY_EXTCONF_H
+# include RUBY_EXTCONF_H
+#endif
+
+#include "ruby/internal/compiler_since.h"
+
+#undef HAVE_PROTOTYPES
+#define HAVE_PROTOTYPES 1
+
+#undef HAVE_STDARG_PROTOTYPES
+#define HAVE_STDARG_PROTOTYPES 1
+
+#undef TOKEN_PASTE
+#define TOKEN_PASTE(x,y) x##y
+
+#if defined(__cplusplus)
+#/* __builtin_choose_expr and __builtin_types_compatible aren't available
+# * on C++. See https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */
+# undef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
+# undef HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P
+
+/* HAVE_VA_ARGS_MACRO is for C. C++ situations might be different. */
+# undef HAVE_VA_ARGS_MACRO
+# if __cplusplus >= 201103L
+# define HAVE_VA_ARGS_MACRO
+# elif defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__
+# define HAVE_VA_ARGS_MACRO
+# elif defined(__INTEL_CXX11_MODE__)
+# define HAVE_VA_ARGS_MACRO
+# elif RBIMPL_COMPILER_IS(MSVC)
+# define HAVE_VA_ARGS_MACRO
+# else
+# /* NG, not known. */
+# endif
+#endif
+
+#if RBIMPL_COMPILER_BEFORE(GCC, 4, 9, 0)
+# /* See https://bugs.ruby-lang.org/issues/14221 */
+# undef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
+#endif
+
+#if RBIMPL_COMPILER_BEFORE(GCC, 5, 0, 0)
+# /* GCC 4.9.2 reportedly has this feature and is broken. The function is not
+# * officially documented below. Seems we should not use it.
+# * https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gcc/Other-Builtins.html */
+# undef HAVE_BUILTIN___BUILTIN_ALLOCA_WITH_ALIGN
+#endif
+
+#if defined(__SUNPRO_CC)
+# /* Oracle Developer Studio 12.5: GCC compatibility guide says it supports
+# * statement expressions. But to our knowledge they support the extension
+# * only for C and not for C++. Prove me wrong. Am happy to support them if
+# * there is a way. */
+# undef HAVE_STMT_AND_DECL_IN_EXPR
+#endif
+
+#ifndef STRINGIZE0
+# define STRINGIZE(expr) STRINGIZE0(expr)
+# define STRINGIZE0(expr) #expr
+#endif
+
+#ifdef AC_APPLE_UNIVERSAL_BUILD
+# undef WORDS_BIGENDIAN
+# ifdef __BIG_ENDIAN__
+# define WORDS_BIGENDIAN
+# endif
+#endif
+
+#ifndef DLEXT_MAXLEN
+# define DLEXT_MAXLEN 4
+#endif
+
+#ifndef RUBY_PLATFORM
+# define RUBY_PLATFORM "unknown-unknown"
+#endif
+
+#ifdef UNALIGNED_WORD_ACCESS
+# /* Take that. */
+#elif defined(__i386)
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__i386__)
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(_M_IX86)
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__x86_64)
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__x86_64__)
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(_M_AMD64)
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__powerpc64__)
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__POWERPC__) // __POWERPC__ is defined for ppc and ppc64 on Darwin
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__aarch64__)
+# define UNALIGNED_WORD_ACCESS 1
+#elif defined(__mc68020__)
+# define UNALIGNED_WORD_ACCESS 1
+#else
+# define UNALIGNED_WORD_ACCESS 0
+#endif
+
+/* Detection of __VA_OPT__ */
+#if ! defined(HAVE_VA_ARGS_MACRO)
+# undef HAVE___VA_OPT__
+
+#elif defined(__cplusplus)
+# if __cplusplus > 201703L
+# define HAVE___VA_OPT__
+# else
+# undef HAVE___VA_OPT__
+# endif
+#else
+# /* Idea taken from: https://stackoverflow.com/a/48045656 */
+# define RBIMPL_TEST3(q, w, e, ...) e
+# define RBIMPL_TEST2(...) RBIMPL_TEST3(__VA_OPT__(,),1,0,0)
+# define RBIMPL_TEST1() RBIMPL_TEST2("ruby")
+# if RBIMPL_TEST1()
+# define HAVE___VA_OPT__
+# else
+# undef HAVE___VA_OPT__
+# endif
+# undef RBIMPL_TEST1
+# undef RBIMPL_TEST2
+# undef RBIMPL_TEST3
+#endif /* HAVE_VA_ARGS_MACRO */
+
+#endif /* RBIMPL_CONFIG_H */
diff --git a/include/ruby/internal/constant_p.h b/include/ruby/internal/constant_p.h
new file mode 100644
index 0000000000..92d69cb972
--- /dev/null
+++ b/include/ruby/internal/constant_p.h
@@ -0,0 +1,38 @@
+#ifndef RBIMPL_CONSTANT_P_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_CONSTANT_P_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_CONSTANT_P.
+ *
+ * Note that __builtin_constant_p can be applicable inside of inline functions,
+ * according to GCC manual. Clang lacks that feature, though.
+ *
+ * @see https://bugs.llvm.org/show_bug.cgi?id=4898
+ * @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
+ */
+#include "ruby/internal/has/builtin.h"
+
+/** Wraps (or simulates) `__builtin_constant_p` */
+#if RBIMPL_HAS_BUILTIN(__builtin_constant_p)
+# define RBIMPL_CONSTANT_P(expr) __builtin_constant_p(expr)
+#else
+# define RBIMPL_CONSTANT_P(expr) 0
+#endif
+
+#endif /* RBIMPL_CONSTANT_P_H */
diff --git a/include/ruby/internal/core.h b/include/ruby/internal/core.h
new file mode 100644
index 0000000000..3f4561c6a6
--- /dev/null
+++ b/include/ruby/internal/core.h
@@ -0,0 +1,35 @@
+#ifndef RBIMPL_CORE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_CORE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Core data structures, definitions and manipulations.
+ */
+#include "ruby/internal/core/rarray.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/core/rbignum.h"
+#include "ruby/internal/core/rclass.h"
+#include "ruby/internal/core/rdata.h"
+#include "ruby/internal/core/rfile.h"
+#include "ruby/internal/core/rhash.h"
+#include "ruby/internal/core/robject.h"
+#include "ruby/internal/core/rregexp.h"
+#include "ruby/internal/core/rstring.h"
+#include "ruby/internal/core/rstruct.h"
+#include "ruby/internal/core/rtypeddata.h"
+#endif /* RBIMPL_CORE_H */
diff --git a/include/ruby/internal/core/rarray.h b/include/ruby/internal/core/rarray.h
new file mode 100644
index 0000000000..73cc0f5dd9
--- /dev/null
+++ b/include/ruby/internal/core/rarray.h
@@ -0,0 +1,405 @@
+#ifndef RBIMPL_RARRAY_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RARRAY_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RArray.
+ */
+#include "ruby/internal/arithmetic/long.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/attr/maybe_unused.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/gc.h"
+#include "ruby/internal/stdbool.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+#include "ruby/assert.h"
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RArray.
+ * @return The passed object casted to ::RArray.
+ */
+#define RARRAY(obj) RBIMPL_CAST((struct RArray *)(obj))
+/** @cond INTERNAL_MACRO */
+#define RARRAY_EMBED_FLAG RARRAY_EMBED_FLAG
+#define RARRAY_EMBED_LEN_MASK RARRAY_EMBED_LEN_MASK
+#define RARRAY_EMBED_LEN_MAX RARRAY_EMBED_LEN_MAX
+#define RARRAY_EMBED_LEN_SHIFT RARRAY_EMBED_LEN_SHIFT
+/** @endcond */
+#define RARRAY_LEN rb_array_len /**< @alias{rb_array_len} */
+#define RARRAY_CONST_PTR rb_array_const_ptr /**< @alias{rb_array_const_ptr} */
+
+/** @cond INTERNAL_MACRO */
+#if defined(__fcc__) || defined(__fcc_version) || \
+ defined(__FCC__) || defined(__FCC_VERSION)
+/* workaround for old version of Fujitsu C Compiler (fcc) */
+# define FIX_CONST_VALUE_PTR(x) ((const VALUE *)(x))
+#else
+# define FIX_CONST_VALUE_PTR(x) (x)
+#endif
+
+#define RARRAY_EMBED_LEN RARRAY_EMBED_LEN
+#define RARRAY_LENINT RARRAY_LENINT
+#define RARRAY_ASET RARRAY_ASET
+#define RARRAY_PTR RARRAY_PTR
+/** @endcond */
+
+/**
+ * @private
+ *
+ * Bits that you can set to ::RBasic::flags.
+ *
+ * @warning These enums are not the only bits we use for arrays.
+ *
+ * @internal
+ *
+ * Unlike strings, flag usages for arrays are scattered across the entire
+ * source codes. @shyouhei doesn't know the complete list. But what is listed
+ * here is at least incomplete.
+ */
+enum ruby_rarray_flags {
+ /* RUBY_FL_USER0 is for ELTS_SHARED */
+
+ /**
+ * This flag has something to do with memory footprint. If the array is
+ * "small" enough, ruby tries to be creative to abuse padding bits of
+ * struct ::RArray for storing its contents. This flag denotes that
+ * situation.
+ *
+ * @warning This bit has to be considered read-only. Setting/clearing
+ * this bit without corresponding fix up must cause immediate
+ * SEGV. Also, internal structures of an array change
+ * dynamically and transparently throughout of its lifetime.
+ * Don't assume it being persistent.
+ *
+ * @internal
+ *
+ * 3rd parties must not be aware that there even is more than one way to
+ * store array elements. It was a bad idea to expose this to them.
+ */
+ RARRAY_EMBED_FLAG = RUBY_FL_USER1,
+
+ /**
+ * When an array employs embedded strategy (see ::RARRAY_EMBED_FLAG), these
+ * bits are used to store the number of elements actually filled into
+ * ::RArray::ary.
+ *
+ * @internal
+ *
+ * 3rd parties must not be aware that there even is more than one way to
+ * store array elements. It was a bad idea to expose this to them.
+ */
+ RARRAY_EMBED_LEN_MASK = RUBY_FL_USER9 | RUBY_FL_USER8 | RUBY_FL_USER7 | RUBY_FL_USER6 |
+ RUBY_FL_USER5 | RUBY_FL_USER4 | RUBY_FL_USER3
+};
+
+/**
+ * This is an enum because GDB wants it (rather than a macro). People need not
+ * bother.
+ */
+enum ruby_rarray_consts {
+ /** Where ::RARRAY_EMBED_LEN_MASK resides. */
+ RARRAY_EMBED_LEN_SHIFT = RUBY_FL_USHIFT + 3
+};
+
+/** Ruby's array. */
+struct RArray {
+
+ /** Basic part, including flags and class. */
+ struct RBasic basic;
+
+ /** Array's specific fields. */
+ union {
+
+ /**
+ * Arrays that use separated memory region for elements use this
+ * pattern.
+ */
+ struct {
+
+ /** Number of elements of the array. */
+ long len;
+
+ /** Auxiliary info. */
+ union {
+
+ /**
+ * Capacity of `*ptr`. A continuous memory region of at least
+ * `capa` elements is expected to exist at `*ptr`. This can be
+ * bigger than `len`.
+ */
+ long capa;
+
+ /**
+ * Parent of the array. Nowadays arrays can share their
+ * backend memory regions each other, constructing gigantic
+ * nest of objects. This situation is called "shared", and
+ * this is the field to control such properties.
+ */
+#if defined(__clang__) /* <- clang++ is sane */ || \
+ !defined(__cplusplus) /* <- C99 is sane */ || \
+ (__cplusplus > 199711L) /* <- C++11 is sane */
+ const
+#endif
+ VALUE shared_root;
+ } aux;
+
+ /**
+ * Pointer to the C array that holds the elements of the array. In
+ * the old days each array had dedicated memory regions. That is
+ * no longer true today, but there still are arrays of such
+ * properties. This field could be used to point such things.
+ */
+ const VALUE *ptr;
+ } heap;
+
+ /**
+ * Embedded elements. When an array is short enough, it uses this area
+ * to store its elements. In this case the length is encoded into the
+ * flags.
+ */
+ /* This is a length 1 array because:
+ * 1. GCC has a bug that does not optimize C flexible array members
+ * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452)
+ * 2. Zero length arrays are not supported by all compilers
+ */
+ const VALUE ary[1];
+ } as;
+};
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * @private
+ *
+ * Declares a section of code where raw pointers are used. This is an
+ * implementation detail of #RARRAY_PTR_USE. People don't use it directly.
+ *
+ * @param[in] ary An object of ::RArray.
+ * @return `ary`'s backend C array.
+ */
+VALUE *rb_ary_ptr_use_start(VALUE ary);
+
+/**
+ * @private
+ *
+ * Declares an end of a section formerly started by rb_ary_ptr_use_start().
+ * This is an implementation detail of #RARRAY_PTR_USE. People don't use it
+ * directly.
+ *
+ * @param[in] a An object of ::RArray.
+ */
+void rb_ary_ptr_use_end(VALUE a);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the length of the array.
+ *
+ * @param[in] ary Array in question.
+ * @return Its number of elements.
+ * @pre `ary` must be an instance of ::RArray, and must has its
+ * ::RARRAY_EMBED_FLAG flag set.
+ *
+ * @internal
+ *
+ * This was a macro before. It was inevitable to be public, since macros are
+ * global constructs. But should it be forever? Now that it is a function,
+ * @shyouhei thinks it could just be eliminated, hidden into implementation
+ * details.
+ */
+static inline long
+RARRAY_EMBED_LEN(VALUE ary)
+{
+ RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY);
+ RBIMPL_ASSERT_OR_ASSUME(RB_FL_ANY_RAW(ary, RARRAY_EMBED_FLAG));
+
+ VALUE f = RBASIC(ary)->flags;
+ f &= RARRAY_EMBED_LEN_MASK;
+ f >>= RARRAY_EMBED_LEN_SHIFT;
+ return RBIMPL_CAST((long)f);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/**
+ * Queries the length of the array.
+ *
+ * @param[in] a Array in question.
+ * @return Its number of elements.
+ * @pre `a` must be an instance of ::RArray.
+ */
+static inline long
+rb_array_len(VALUE a)
+{
+ RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
+
+ if (RB_FL_ANY_RAW(a, RARRAY_EMBED_FLAG)) {
+ return RARRAY_EMBED_LEN(a);
+ }
+ else {
+ return RARRAY(a)->as.heap.len;
+ }
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Identical to rb_array_len(), except it differs for the return type.
+ *
+ * @param[in] ary Array in question.
+ * @exception rb_eRangeError Too long.
+ * @return Its number of elements.
+ * @pre `ary` must be an instance of ::RArray.
+ *
+ * @internal
+ *
+ * This API seems redundant but has actual usages.
+ */
+static inline int
+RARRAY_LENINT(VALUE ary)
+{
+ return rb_long2int(RARRAY_LEN(ary));
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/**
+ * @private
+ *
+ * This is an implementation detail of RARRAY_PTR(). People do not use it
+ * directly.
+ *
+ * @param[in] a An object of ::RArray.
+ * @return Its backend storage.
+ */
+static inline const VALUE *
+rb_array_const_ptr(VALUE a)
+{
+ RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
+
+ if (RB_FL_ANY_RAW(a, RARRAY_EMBED_FLAG)) {
+ return FIX_CONST_VALUE_PTR(RARRAY(a)->as.ary);
+ }
+ else {
+ return FIX_CONST_VALUE_PTR(RARRAY(a)->as.heap.ptr);
+ }
+}
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #RARRAY_PTR_USE. People do not use it
+ * directly.
+ */
+#define RBIMPL_RARRAY_STMT(ary, var, expr) do { \
+ RBIMPL_ASSERT_TYPE((ary), RUBY_T_ARRAY); \
+ const VALUE rbimpl_ary = (ary); \
+ VALUE *var = rb_ary_ptr_use_start(rbimpl_ary); \
+ expr; \
+ rb_ary_ptr_use_end(rbimpl_ary); \
+} while (0)
+
+/**
+ * Declares a section of code where raw pointers are used. In case you need to
+ * touch the raw C array instead of polite CAPIs, then that operation shall be
+ * wrapped using this macro.
+ *
+ * ```CXX
+ * const auto ary = rb_eval_string("[...]");
+ * const auto len = RARRAY_LENINT(ary);
+ * const auto symwrite = rb_intern("write");
+ *
+ * RARRAY_PTR_USE(ary, ptr, {
+ * rb_funcallv(rb_stdout, symwrite, len, ptr);
+ * });
+ * ```
+ *
+ * @param ary An object of ::RArray.
+ * @param ptr_name A variable name which points the C array in `expr`.
+ * @param expr The expression that touches `ptr_name`.
+ *
+ * @internal
+ *
+ * For historical reasons use of this macro is not enforced. There are
+ * extension libraries in the wild which call RARRAY_PTR() without it. We want
+ * them use it... Maybe some transition path can be implemented later.
+ */
+#define RARRAY_PTR_USE(ary, ptr_name, expr) \
+ RBIMPL_RARRAY_STMT(ary, ptr_name, expr)
+
+/**
+ * Wild use of a C pointer. This function accesses the backend storage
+ * directly. This is slower than #RARRAY_PTR_USE. It exercises
+ * extra manoeuvres to protect our generational GC. Use of this function is
+ * considered archaic. Use a modern way instead.
+ *
+ * @param[in] ary An object of ::RArray.
+ * @return The backend C array.
+ *
+ * @internal
+ *
+ * That said... there are extension libraries in the wild who uses it. We
+ * cannot but continue supporting.
+ */
+static inline VALUE *
+RARRAY_PTR(VALUE ary)
+{
+ RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY);
+
+ VALUE tmp = RB_OBJ_WB_UNPROTECT_FOR(ARRAY, ary);
+ return RBIMPL_CAST((VALUE *)RARRAY_CONST_PTR(tmp));
+}
+
+/**
+ * Assigns an object in an array.
+ *
+ * @param[out] ary Destination array object.
+ * @param[in] i Index of `ary`.
+ * @param[in] v Arbitrary ruby object.
+ * @pre `ary` must be an instance of ::RArray.
+ * @pre `ary`'s length must be longer than or equal to `i`.
+ * @pre `i` must be greater than or equal to zero.
+ * @post `ary`'s `i`th element is set to `v`.
+ */
+static inline void
+RARRAY_ASET(VALUE ary, long i, VALUE v)
+{
+ RARRAY_PTR_USE(ary, ptr,
+ RB_OBJ_WRITE(ary, &ptr[i], v));
+}
+
+/**
+ * @deprecated
+ *
+ * :FIXME: we want to convert RARRAY_AREF into an inline function (to add rooms
+ * for more sanity checks). However there were situations where the address of
+ * this macro is taken i.e. &RARRAY_AREF(...). They cannot be possible if this
+ * is not a macro. Such usages are abuse, and we eliminated them internally.
+ * However we are afraid of similar things to remain in the wild. This macro
+ * remains as it is due to that. If we could warn such usages we can set a
+ * transition path, but currently no way is found to do so.
+ */
+#define RARRAY_AREF(a, i) RARRAY_CONST_PTR(a)[i]
+
+#endif /* RBIMPL_RARRAY_H */
diff --git a/include/ruby/internal/core/rbasic.h b/include/ruby/internal/core/rbasic.h
new file mode 100644
index 0000000000..63cdff8e09
--- /dev/null
+++ b/include/ruby/internal/core/rbasic.h
@@ -0,0 +1,172 @@
+#ifndef RBIMPL_RBASIC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RBASIC_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RBasic.
+ */
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/attr/forceinline.h"
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/internal/value.h"
+#include "ruby/assert.h"
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj Arbitrary Ruby object.
+ * @return The passed object casted to ::RBasic.
+ */
+#define RBASIC(obj) RBIMPL_CAST((struct RBasic *)(obj))
+/** @cond INTERNAL_MACRO */
+#define RBASIC_CLASS RBASIC_CLASS
+#define RBIMPL_RVALUE_EMBED_LEN_MAX 3
+#define RVALUE_EMBED_LEN_MAX RVALUE_EMBED_LEN_MAX
+#define RBIMPL_EMBED_LEN_MAX_OF(T) \
+ RBIMPL_CAST((int)(sizeof(VALUE[RBIMPL_RVALUE_EMBED_LEN_MAX]) / (sizeof(T))))
+/** @endcond */
+
+/**
+ * This is an enum because GDB wants it (rather than a macro). People need not
+ * bother.
+ */
+enum ruby_rvalue_flags {
+ /** Max possible number of objects that can be embedded. */
+ RVALUE_EMBED_LEN_MAX = RBIMPL_RVALUE_EMBED_LEN_MAX
+};
+
+#if (SIZEOF_VALUE < SIZEOF_UINT64_T)
+#define RBASIC_SHAPE_ID_FIELD 1
+#else
+#define RBASIC_SHAPE_ID_FIELD 0
+#endif
+
+/**
+ * Ruby object's base components. All Ruby objects have them in common.
+ */
+struct
+RUBY_ALIGNAS(SIZEOF_VALUE)
+RBasic {
+
+ /**
+ * Per-object flags. Each Ruby object has its own characteristics apart
+ * from its class. For instance, whether an object is frozen or not is not
+ * controlled by its class. This is where such properties are stored.
+ *
+ * @see enum ::ruby_fl_type
+ *
+ * @note This is ::VALUE rather than an enum for alignment purposes. Back
+ * in the 1990s there were no such thing like `_Alignas` in C.
+ */
+ VALUE flags;
+
+ /**
+ * Class of an object. Every object has its class. Also, everything is an
+ * object in Ruby. This means classes are also objects. Classes have
+ * their own classes, classes of classes have their classes too, and it
+ * recursively continues forever.
+ *
+ * Also note the `const` qualifier. In Ruby, an object cannot "change" its
+ * class.
+ */
+ const VALUE klass;
+
+#if RBASIC_SHAPE_ID_FIELD
+ VALUE shape_id;
+#endif
+
+#ifdef __cplusplus
+ public:
+ RBIMPL_ATTR_CONSTEXPR(CXX11)
+ RBIMPL_ATTR_ARTIFICIAL()
+ RBIMPL_ATTR_FORCEINLINE()
+ RBIMPL_ATTR_NOALIAS()
+ /**
+ * We need to define this explicit constructor because the field `klass` is
+ * const-qualified above, which effectively defines the implicit default
+ * constructor as "deleted" (as of C++11) -- No way but to define one by
+ * ourselves.
+ */
+ RBasic() :
+ flags(RBIMPL_VALUE_NULL),
+ klass(RBIMPL_VALUE_NULL)
+#if RBASIC_SHAPE_ID_FIELD
+ , shape_id(RBIMPL_VALUE_NULL)
+#endif
+ {
+ }
+# define RBASIC_INIT RBasic()
+#else
+# define RBASIC_INIT {RBIMPL_VALUE_NULL}
+#endif
+};
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * Make the object invisible from Ruby code.
+ *
+ * It is useful to let Ruby's GC manage your internal data structure -- The
+ * object keeps being managed by GC, but `ObjectSpace.each_object` never yields
+ * the object.
+ *
+ * Note that the object also lose a way to call a method on it.
+ *
+ * @param[out] obj A Ruby object.
+ * @return The passed object.
+ * @post The object is destructively modified to be invisible.
+ * @see rb_obj_reveal
+ */
+VALUE rb_obj_hide(VALUE obj);
+
+/**
+ * Make a hidden object visible again.
+ *
+ * It is the caller's responsibility to pass the right `klass` which `obj`
+ * originally used to belong to.
+ *
+ * @param[out] obj A Ruby object.
+ * @param[in] klass Class of `obj`.
+ * @return Passed `obj`.
+ * @pre `obj` was previously hidden.
+ * @post `obj`'s class is `klass`.
+ * @see rb_obj_hide
+ */
+VALUE rb_obj_reveal(VALUE obj, VALUE klass); /* do not use this API to change klass information */
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the class of an object.
+ *
+ * @param[in] obj An object.
+ * @return Its class.
+ */
+static inline VALUE
+RBASIC_CLASS(VALUE obj)
+{
+ RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(obj));
+ return RBASIC(obj)->klass;
+}
+
+#endif /* RBIMPL_RBASIC_H */
diff --git a/include/ruby/internal/core/rbignum.h b/include/ruby/internal/core/rbignum.h
new file mode 100644
index 0000000000..1d31743235
--- /dev/null
+++ b/include/ruby/internal/core/rbignum.h
@@ -0,0 +1,80 @@
+#ifndef RBIMPL_RBIGNUM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RBIGNUM_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate struct RBignum.
+ * @note The struct RBignum itself is opaque.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+#include "ruby/internal/stdbool.h"
+
+#define RBIGNUM_SIGN rb_big_sign /**< @alias{rb_big_sign} */
+
+/** @cond INTERNAL_MACRO */
+#define RBIGNUM_POSITIVE_P RBIGNUM_POSITIVE_P
+#define RBIGNUM_NEGATIVE_P RBIGNUM_NEGATIVE_P
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * The "sign" of a bignum.
+ *
+ * @param[in] num An object of RBignum.
+ * @retval 1 It is greater than or equal to zero.
+ * @retval 0 It is less than zero.
+ *
+ * @internal
+ *
+ * Implementation wise, unlike fixnums (which are 2's complement), bignums are
+ * signed magnitude system. Theoretically it could be possible to have
+ * negative zero instances. But in reality there is no way to create such
+ * thing. Nobody ever needed that kind of insanity.
+ */
+int rb_big_sign(VALUE num);
+RBIMPL_SYMBOL_EXPORT_END()
+
+/**
+ * Checks if the bignum is positive.
+ * @param[in] b An object of RBignum.
+ * @retval false `b` is less than zero.
+ * @retval true Otherwise.
+ */
+static inline bool
+RBIGNUM_POSITIVE_P(VALUE b)
+{
+ RBIMPL_ASSERT_TYPE(b, RUBY_T_BIGNUM);
+ return RBIGNUM_SIGN(b);
+}
+
+/**
+ * Checks if the bignum is negative.
+ * @param[in] b An object of RBignum.
+ * @retval true `b` is less than zero.
+ * @retval false Otherwise.
+ */
+static inline bool
+RBIGNUM_NEGATIVE_P(VALUE b)
+{
+ RBIMPL_ASSERT_TYPE(b, RUBY_T_BIGNUM);
+ return ! RBIGNUM_POSITIVE_P(b);
+}
+
+#endif /* RBIMPL_RBIGNUM_H */
diff --git a/include/ruby/internal/core/rclass.h b/include/ruby/internal/core/rclass.h
new file mode 100644
index 0000000000..6f78cc569b
--- /dev/null
+++ b/include/ruby/internal/core/rclass.h
@@ -0,0 +1,93 @@
+#ifndef RBIMPL_RCLASS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RCLASS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate struct RClass.
+ * @note The struct RClass itself is opaque.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/cast.h"
+
+/** @cond INTERNAL_MACRO */
+#define RMODULE_IS_REFINEMENT RMODULE_IS_REFINEMENT
+/** @endcond */
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an RClass.
+ * @return The passed object casted to RClass.
+ */
+#define RCLASS(obj) RBIMPL_CAST((struct RClass *)(obj))
+
+/** @alias{RCLASS} */
+#define RMODULE RCLASS
+
+/** @alias{rb_class_get_superclass} */
+#define RCLASS_SUPER rb_class_get_superclass
+
+/**
+ * @private
+ *
+ * Bits that you can set to ::RBasic::flags.
+ *
+ * @internal
+ *
+ * Why is it here, given RClass itself is not?
+ */
+enum ruby_rmodule_flags {
+ /**
+ * This flag has something to do with refinements. A module created using
+ * rb_mod_refine() has this flag set. This is the bit which controls
+ * difference between normal inclusion versus refinements.
+ */
+ RMODULE_IS_REFINEMENT = RUBY_FL_USER1
+};
+
+struct RClass; /* Opaque, declared here for RCLASS() macro. */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * Returns the superclass of a class.
+ * @param[in] klass An object of RClass.
+ * @retval RUBY_Qfalse `klass` has no super class.
+ * @retval otherwise Raw superclass of `klass`
+ * @see rb_class_superclass
+ *
+ * ### Q&A ###
+ *
+ * - Q: How can a class have no super class?
+ *
+ * - A: `klass` could be a module. Or it could be ::rb_cBasicObject.
+ *
+ * - Q: What do you mean by "raw" superclass?
+ *
+ * - A: This is a really good question. The answer is that this function
+ * returns something different from what you would normally expect. On
+ * occasions ruby inserts hidden classes in a hierarchy of class
+ * inheritance behind-the-scene. Such classes are called "iclass"es and
+ * distinguished using ::RUBY_T_ICLASS in C level. They are truly
+ * transparent from Ruby level but can be accessed from C, by using this
+ * API.
+ */
+VALUE rb_class_get_superclass(VALUE klass);
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_RCLASS_H */
diff --git a/include/ruby/internal/core/rdata.h b/include/ruby/internal/core/rdata.h
new file mode 100644
index 0000000000..cee5e7b5ea
--- /dev/null
+++ b/include/ruby/internal/core/rdata.h
@@ -0,0 +1,363 @@
+#ifndef RBIMPL_RDATA_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RDATA_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RData.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/warning.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+#include "ruby/defines.h"
+
+/** @cond INTERNAL_MACRO */
+#ifndef RUBY_UNTYPED_DATA_WARNING
+#define RUBY_UNTYPED_DATA_WARNING 1
+#endif
+
+#define RBIMPL_DATA_FUNC(f) RBIMPL_CAST((void (*)(void *))(f))
+#define RBIMPL_ATTRSET_UNTYPED_DATA_FUNC() \
+ RBIMPL_ATTR_WARNING(("untyped Data is unsafe; use TypedData instead")) \
+ RBIMPL_ATTR_DEPRECATED(("by TypedData"))
+
+#define RBIMPL_MACRO_SELECT(x, y) x ## y
+#define RUBY_MACRO_SELECT(x, y) RBIMPL_MACRO_SELECT(x, y)
+/** @endcond */
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RData.
+ * @return The passed object casted to ::RData.
+ */
+#define RDATA(obj) RBIMPL_CAST((struct RData *)(obj))
+
+/**
+ * Convenient getter macro.
+ *
+ * @param obj An object, which is in fact an ::RData.
+ * @return The passed object's ::RData::data field.
+ */
+#define DATA_PTR(obj) RDATA(obj)->data
+
+/**
+ * This is a value you can set to ::RData::dfree. Setting this means the data
+ * was allocated using ::ruby_xmalloc() (or variants), and shall be freed using
+ * ::ruby_xfree().
+ *
+ * @warning Do not use this if you want to use system malloc, because the
+ * system and Ruby might or might not share the same malloc
+ * implementation.
+ */
+#define RUBY_DEFAULT_FREE RBIMPL_DATA_FUNC(-1)
+
+/**
+ * This is a value you can set to ::RData::dfree. Setting this means the data
+ * is managed by someone else, like, statically allocated. Of course you are
+ * on your own then.
+ */
+#define RUBY_NEVER_FREE RBIMPL_DATA_FUNC(0)
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define RUBY_UNTYPED_DATA_FUNC(f) f RBIMPL_ATTRSET_UNTYPED_DATA_FUNC()
+
+/*
+#define RUBY_DATA_FUNC(func) ((void (*)(void*))(func))
+*/
+
+/**
+ * This is the type of callbacks registered to ::RData. The argument is the
+ * `data` field.
+ */
+typedef void (*RUBY_DATA_FUNC)(void*);
+
+/**
+ * @deprecated
+ *
+ * Old "untyped" user data. It has roughly the same usage as struct
+ * ::RTypedData, but lacked several features such as support for compaction GC.
+ * Use of this struct is not recommended any longer. If it is dead necessary,
+ * please inform the core devs about your usage.
+ *
+ * @internal
+ *
+ * @shyouhei tried to add RBIMPL_ATTR_DEPRECATED for this type but that yielded
+ * too many warnings in the core. Maybe we want to retry later... Just add
+ * deprecated document for now.
+ */
+struct RData {
+
+ /** Basic part, including flags and class. */
+ struct RBasic basic;
+
+ /**
+ * This function is called when the object is experiencing GC marks. If it
+ * contains references to other Ruby objects, you need to mark them also.
+ * Otherwise GC will smash your data.
+ *
+ * @see rb_gc_mark()
+ * @warning This is called during GC runs. Object allocations are
+ * impossible at that moment (that is why GC runs).
+ */
+ RUBY_DATA_FUNC dmark;
+
+ /**
+ * This function is called when the object is no longer used. You need to
+ * do whatever necessary to avoid memory leaks.
+ *
+ * @warning This is called during GC runs. Object allocations are
+ * impossible at that moment (that is why GC runs).
+ */
+ RUBY_DATA_FUNC dfree;
+
+ /** Pointer to the actual C level struct that you want to wrap.
+ * This is after dmark and dfree to allow DATA_PTR to continue to work for
+ * both RData and non-embedded RTypedData.
+ */
+ void *data;
+};
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * This is the primitive way to wrap an existing C struct into ::RData.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] datap Pointer to the target C struct.
+ * @param[in] dmark Mark function.
+ * @param[in] dfree Free function.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return An allocated object that wraps `datap`.
+ */
+VALUE rb_data_object_wrap(VALUE klass, void *datap, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree);
+
+/**
+ * Identical to rb_data_object_wrap(), except it allocates a new data region
+ * internally instead of taking an existing one. The allocation is done using
+ * ruby_calloc(). Hence it makes no sense to pass anything other than
+ * ::RUBY_DEFAULT_FREE to the last argument.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] size Requested size of memory to allocate.
+ * @param[in] dmark Mark function.
+ * @param[in] dfree Free function.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return An allocated object that wraps a new `size` byte region.
+ */
+VALUE rb_data_object_zalloc(VALUE klass, size_t size, RUBY_DATA_FUNC dmark, RUBY_DATA_FUNC dfree);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/**
+ * Converts sval, a pointer to your struct, into a Ruby object.
+ *
+ * @param klass A ruby level class.
+ * @param mark Mark function.
+ * @param free Free function.
+ * @param sval A pointer to your struct.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ */
+#define Data_Wrap_Struct(klass, mark, free, sval) \
+ rb_data_object_wrap( \
+ (klass), \
+ (sval), \
+ RBIMPL_DATA_FUNC(mark), \
+ RBIMPL_DATA_FUNC(free))
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #Data_Make_Struct. People don't use it
+ * directly.
+ *
+ * @param result Variable name of created Ruby object.
+ * @param klass Ruby level class of the object.
+ * @param type Type name of the C struct.
+ * @param size Size of the C struct.
+ * @param mark Mark function.
+ * @param free Free function.
+ * @param sval Variable name of created C struct.
+ */
+#define Data_Make_Struct0(result, klass, type, size, mark, free, sval) \
+ VALUE result = rb_data_object_zalloc( \
+ (klass), \
+ (size), \
+ RBIMPL_DATA_FUNC(mark), \
+ RBIMPL_DATA_FUNC(free)); \
+ (sval) = RBIMPL_CAST((type *)DATA_PTR(result)); \
+ RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval))
+
+/**
+ * Identical to #Data_Wrap_Struct, except it allocates a new data region
+ * internally instead of taking an existing one. The allocation is done using
+ * ruby_calloc(). Hence it makes no sense to pass anything other than
+ * ::RUBY_DEFAULT_FREE to the `free` argument.
+ *
+ * @param klass Ruby level class of the returning object.
+ * @param type Type name of the C struct.
+ * @param mark Mark function.
+ * @param free Free function.
+ * @param sval Variable name of created C struct.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ */
+#ifdef HAVE_STMT_AND_DECL_IN_EXPR
+#define Data_Make_Struct(klass, type, mark, free, sval) \
+ RB_GNUC_EXTENSION({ \
+ Data_Make_Struct0( \
+ data_struct_obj, \
+ klass, \
+ type, \
+ sizeof(type), \
+ mark, \
+ free, \
+ sval); \
+ data_struct_obj; \
+ })
+#else
+#define Data_Make_Struct(klass, type, mark, free, sval) \
+ rb_data_object_make( \
+ (klass), \
+ RBIMPL_DATA_FUNC(mark), \
+ RBIMPL_DATA_FUNC(free), \
+ RBIMPL_CAST((void **)&(sval)), \
+ sizeof(type))
+#endif
+
+/**
+ * Obtains a C struct from inside of a wrapper Ruby object.
+ *
+ * @param obj An instance of ::RData.
+ * @param type Type name of the C struct.
+ * @param sval Variable name of obtained C struct.
+ * @return Unwrapped C struct that `obj` holds.
+ */
+#define Data_Get_Struct(obj, type, sval) \
+ ((sval) = RBIMPL_CAST((type*)rb_data_object_get(obj)))
+
+RBIMPL_ATTRSET_UNTYPED_DATA_FUNC()
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_data_object_wrap(). People don't use
+ * it directly.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] ptr Pointer to the target C struct.
+ * @param[in] mark Mark function.
+ * @param[in] free Free function.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return An allocated object that wraps `datap`.
+ */
+static inline VALUE
+rb_data_object_wrap_warning(VALUE klass, void *ptr, RUBY_DATA_FUNC mark, RUBY_DATA_FUNC free)
+{
+ return rb_data_object_wrap(klass, ptr, mark, free);
+}
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #Data_Get_Struct. People don't use it
+ * directly.
+ *
+ * @param[in] obj An instance of ::RData.
+ * @return Unwrapped C struct that `obj` holds.
+ */
+static inline void *
+rb_data_object_get(VALUE obj)
+{
+ Check_Type(obj, RUBY_T_DATA);
+ return DATA_PTR(obj);
+}
+
+RBIMPL_ATTRSET_UNTYPED_DATA_FUNC()
+/**
+ * @private
+ *
+ * This is an implementation detail of #Data_Get_Struct. People don't use it
+ * directly.
+ *
+ * @param[in] obj An instance of ::RData.
+ * @return Unwrapped C struct that `obj` holds.
+ */
+static inline void *
+rb_data_object_get_warning(VALUE obj)
+{
+ return rb_data_object_get(obj);
+}
+
+/**
+ * This is an implementation detail of #Data_Make_Struct. People don't use it
+ * directly.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] mark_func Mark function.
+ * @param[in] free_func Free function.
+ * @param[in] datap Variable of created C struct.
+ * @param[in] size Requested size of allocation.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ * @post `*datap` holds the created C struct.
+ */
+static inline VALUE
+rb_data_object_make(VALUE klass, RUBY_DATA_FUNC mark_func, RUBY_DATA_FUNC free_func, void **datap, size_t size)
+{
+ Data_Make_Struct0(result, klass, void, size, mark_func, free_func, *datap);
+ return result;
+}
+
+/** @cond INTERNAL_MACRO */
+#define rb_data_object_wrap_0 rb_data_object_wrap
+#define rb_data_object_wrap_1 rb_data_object_wrap_warning
+#define rb_data_object_wrap_2 rb_data_object_wrap_ /* Used here vvvv */
+#define rb_data_object_wrap RUBY_MACRO_SELECT(rb_data_object_wrap_2, RUBY_UNTYPED_DATA_WARNING)
+#define rb_data_object_get_0 rb_data_object_get
+#define rb_data_object_get_1 rb_data_object_get_warning
+#define rb_data_object_get_2 rb_data_object_get_ /* Used here vvvv */
+#define rb_data_object_get RUBY_MACRO_SELECT(rb_data_object_get_2, RUBY_UNTYPED_DATA_WARNING)
+#define rb_data_object_make_0 rb_data_object_make
+#define rb_data_object_make_1 rb_data_object_make_warning
+#define rb_data_object_make_2 rb_data_object_make_ /* Used here vvvv */
+#define rb_data_object_make RUBY_MACRO_SELECT(rb_data_object_make_2, RUBY_UNTYPED_DATA_WARNING)
+/** @endcond */
+#endif /* RBIMPL_RDATA_H */
diff --git a/include/ruby/internal/core/rfile.h b/include/ruby/internal/core/rfile.h
new file mode 100644
index 0000000000..a0eb8cb833
--- /dev/null
+++ b/include/ruby/internal/core/rfile.h
@@ -0,0 +1,51 @@
+#ifndef RBIMPL_RFILE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RFILE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RFile.
+ */
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/cast.h"
+
+/* rb_io_t is in ruby/io.h. The header file has historically not been included
+ * into ruby/ruby.h. We follow that tradition. */
+struct rb_io;
+
+/**
+ * Ruby's File and IO. Ruby's IO are not just file descriptors. They have
+ * buffers. They also have encodings. Various information are controlled
+ * using this struct.
+ */
+struct RFile {
+
+ /** Basic part, including flags and class. */
+ struct RBasic basic;
+
+ /** IO's specific fields. */
+ struct rb_io *fptr;
+};
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RFile.
+ * @return The passed object casted to ::RFile.
+ */
+#define RFILE(obj) RBIMPL_CAST((struct RFile *)(obj))
+#endif /* RBIMPL_RFILE_H */
diff --git a/include/ruby/internal/core/rhash.h b/include/ruby/internal/core/rhash.h
new file mode 100644
index 0000000000..897c570794
--- /dev/null
+++ b/include/ruby/internal/core/rhash.h
@@ -0,0 +1,131 @@
+#ifndef RBIMPL_RHASH_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RHASH_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate struct RHash.
+ * @note The struct RHash itself is opaque.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#if !defined RUBY_EXPORT && !defined RUBY_NO_OLD_COMPATIBILITY
+# include "ruby/backward.h"
+#endif
+
+/**
+ * Retrieves the internal table.
+ *
+ * @param[in] h An instance of RHash.
+ * @pre `h` must be of ::RUBY_T_HASH.
+ * @return A struct st_table which has the contents of this hash.
+ * @note Nowadays as Ruby evolved over ages, RHash has multiple backend
+ * storage engines. `h`'s backend is not guaranteed to be a
+ * st_table. This function creates one when necessary.
+ */
+#define RHASH_TBL(h) rb_hash_tbl(h, __FILE__, __LINE__)
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ *
+ * @internal
+ *
+ * Declaration of rb_hash_ifnone() is at include/ruby/backward.h.
+ */
+#define RHASH_IFNONE(h) rb_hash_ifnone(h)
+
+/**
+ * Queries the size of the hash. Size here means the number of keys that the
+ * hash stores.
+ *
+ * @param[in] h An instance of RHash.
+ * @pre `h` must be of ::RUBY_T_HASH.
+ * @return The size of the hash.
+ */
+#define RHASH_SIZE(h) rb_hash_size_num(h)
+
+/**
+ * Checks if the hash is empty.
+ *
+ * @param[in] h An instance of RHash.
+ * @pre `h` must be of ::RUBY_T_HASH.
+ * @retval true It is.
+ * @retval false It isn't.
+ */
+#define RHASH_EMPTY_P(h) (RHASH_SIZE(h) == 0)
+
+/**
+ * Destructively updates the default value of the hash.
+ *
+ * @param[out] h An instance of RHash.
+ * @param[in] ifnone Arbitrary default value.
+ * @pre `h` must be of ::RUBY_T_HASH.
+ *
+ * @internal
+ *
+ * But why you can set this, given rb_hash_ifnone() doesn't exist?
+ */
+#define RHASH_SET_IFNONE(h, ifnone) rb_hash_set_ifnone((VALUE)h, ifnone)
+
+struct st_table; /* in ruby/st.h */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * This is the implementation detail of #RHASH_SIZE. People don't call this
+ * directly.
+ *
+ * @param[in] hash An instance of RHash.
+ * @pre `hash` must be of ::RUBY_T_HASH.
+ * @return The size of the hash.
+ */
+size_t rb_hash_size_num(VALUE hash);
+
+/**
+ * This is the implementation detail of #RHASH_TBL. People don't call this
+ * directly.
+ *
+ * @param[in] hash An instance of RHash.
+ * @param[in] file The `__FILE__`.
+ * @param[in] line The `__LINE__`.
+ * @pre `hash` must be of ::RUBY_T_HASH.
+ * @return Table that has the contents of the hash.
+ */
+struct st_table *rb_hash_tbl(VALUE hash, const char *file, int line);
+
+/**
+ * This is the implementation detail of #RHASH_SET_IFNONE. People don't call
+ * this directly.
+ *
+ * @param[out] hash An instance of RHash.
+ * @param[in] ifnone Arbitrary default value.
+ * @pre `hash` must be of ::RUBY_T_HASH.
+ */
+VALUE rb_hash_set_ifnone(VALUE hash, VALUE ifnone);
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_RHASH_H */
diff --git a/include/ruby/internal/core/rmatch.h b/include/ruby/internal/core/rmatch.h
new file mode 100644
index 0000000000..a528c2999e
--- /dev/null
+++ b/include/ruby/internal/core/rmatch.h
@@ -0,0 +1,144 @@
+#ifndef RBIMPL_RMATCH_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RMATCH_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RMatch.
+ */
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+#include "ruby/assert.h"
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RMatch.
+ * @return The passed object casted to ::RMatch.
+ */
+#define RMATCH(obj) RBIMPL_CAST((struct RMatch *)(obj))
+/** @cond INTERNAL_MACRO */
+#define RMATCH_REGS RMATCH_REGS
+/** @endcond */
+
+struct re_patter_buffer; /* a.k.a. OnigRegexType, defined in onigmo.h */
+struct re_registers; /* Also in onigmo.h */
+
+/**
+ * @old{re_pattern_buffer}
+ *
+ * @internal
+ *
+ * @shyouhei wonders: is anyone actively using this typedef ...?
+ */
+typedef struct re_pattern_buffer Regexp;
+
+/**
+ * Represents the region of a capture group. This is basically for caching
+ * purpose. re_registers have similar concepts (`beg` and `end`) but they are
+ * in `ptrdiff_t*`. In order for us to implement `MatchData#offset` that info
+ * has to be converted to offset integers. This is the struct to hold such
+ * things.
+ *
+ * @internal
+ *
+ * But why on earth it has to be visible from extension libraries?
+ */
+struct rmatch_offset {
+ long beg; /**< Beginning of a group. */
+ long end; /**< End of a group. */
+};
+
+/** Represents a match. */
+struct rb_matchext_struct {
+ /**
+ * "Registers" of a match. This is a quasi-opaque struct that holds
+ * execution result of a match. Roughly resembles `&~`.
+ */
+ struct re_registers regs;
+
+ /** Capture group offsets, in C array. */
+ struct rmatch_offset *char_offset;
+
+ /** Number of ::rmatch_offset that ::rmatch::char_offset holds. */
+ int char_offset_num_allocated;
+};
+
+typedef struct rb_matchext_struct rb_matchext_t;
+
+/**
+ * Regular expression execution context. When a regular expression "matches"
+ * to a string, it generates capture groups etc. This struct holds that info.
+ * Visible from Ruby as an instance of `MatchData`.
+ *
+ * @note There is no way for extension libraries to manually generate this
+ * struct except by actually exercising the match operation of a regular
+ * expression.
+ */
+struct RMatch {
+
+ /** Basic part, including flags and class. */
+ struct RBasic basic;
+
+ /**
+ * The target string that the match was made against.
+ */
+ VALUE str;
+
+ /**
+ * The expression of this match.
+ */
+ VALUE regexp; /* RRegexp */
+};
+
+#define RMATCH_EXT(m) ((rb_matchext_t *)((char *)(m) + sizeof(struct RMatch)))
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the raw ::re_registers.
+ *
+ * @param[in] match A match object
+ * @pre `match` must be of ::RMatch.
+ * @return Its execution result.
+ * @note Good. So you are aware of the fact that it could return NULL.
+ * Yes. It actually does. This is a really bizarre thing. The
+ * situation is about `String#gsub` and its family. They take
+ * strings as arguments, like `"foo".sub("bar", "baz")`. On such
+ * situations, in order to optimise memory allocations, these
+ * methods do not involve regular expressions at all. They just
+ * sequentially scan the receiver. Okay. The story begins here.
+ * Even when they do not kick our regexp engine, there must be
+ * backref objects e.g. `$&`. But how? You know what? Ruby fakes
+ * them. It allocates an empty ::RMatch and behaves as if there
+ * were execution contexts. In reality there weren't. No
+ * ::re_registers are allocated then. There is no way for this
+ * function but to return NULL for those fake ::RMatch. This is
+ * the reason for the nullability of this function.
+ */
+static inline struct re_registers *
+RMATCH_REGS(VALUE match)
+{
+ RBIMPL_ASSERT_TYPE(match, RUBY_T_MATCH);
+ return &RMATCH_EXT(match)->regs;
+}
+
+#endif /* RBIMPL_RMATCH_H */
diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h
new file mode 100644
index 0000000000..99f6470ac1
--- /dev/null
+++ b/include/ruby/internal/core/robject.h
@@ -0,0 +1,142 @@
+#ifndef RBIMPL_ROBJECT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ROBJECT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RObject.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RObject.
+ * @return The passed object casted to ::RObject.
+ */
+#define ROBJECT(obj) RBIMPL_CAST((struct RObject *)(obj))
+/** @cond INTERNAL_MACRO */
+#define ROBJECT_EMBED_LEN_MAX ROBJECT_EMBED_LEN_MAX
+#define ROBJECT_HEAP ROBJECT_HEAP
+#define ROBJECT_FIELDS_CAPACITY ROBJECT_FIELDS_CAPACITY
+#define ROBJECT_FIELDS ROBJECT_FIELDS
+/** @endcond */
+
+/**
+ * @private
+ *
+ * Bits that you can set to ::RBasic::flags.
+ */
+enum ruby_robject_flags {
+ /**
+ * This flag marks that the object's instance variables are stored in an
+ * external heap buffer.
+ * Normally, instance variable references are stored inside the object slot,
+ * but if it overflow, Ruby may have to allocate a separate buffer and spills
+ * the instance variables there.
+ * This flag denotes that situation.
+ *
+ * @warning This bit has to be considered read-only. Setting/clearing
+ * this bit without corresponding fix up must cause immediate
+ * SEGV. Also, internal structures of an object change
+ * dynamically and transparently throughout of its lifetime.
+ * Don't assume it being persistent.
+ *
+ * @internal
+ *
+ * 3rd parties must not be aware that there even is more than one way to
+ * store instance variables. Might better be hidden.
+ */
+ ROBJECT_HEAP = RUBY_FL_USER4
+};
+
+struct st_table;
+
+/**
+ * Ruby's ordinal objects. Unless otherwise special cased, all predefined and
+ * user-defined classes share this struct to hold their instances.
+ */
+struct RObject {
+
+ /** Basic part, including flags and class. */
+ struct RBasic basic;
+
+ /** Object's specific fields. */
+ union {
+
+ /**
+ * Object that use separated memory region for instance variables use
+ * this pattern.
+ */
+ struct {
+ /** Pointer to a C array that holds instance variables. */
+ VALUE *fields;
+ } heap;
+
+ /* Embedded instance variables. When an object is small enough, it
+ * uses this area to store the instance variables.
+ *
+ * This is a length 1 array because:
+ * 1. GCC has a bug that does not optimize C flexible array members
+ * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452)
+ * 2. Zero length arrays are not supported by all compilers
+ */
+ VALUE ary[1];
+ } as;
+};
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the instance variables.
+ *
+ * @param[in] obj Object in question.
+ * @return Its instance variables, in C array.
+ * @pre `obj` must be an instance of ::RObject.
+ *
+ * @internal
+ *
+ * @shyouhei finds no reason for this to be visible from extension libraries.
+ */
+static inline VALUE *
+ROBJECT_FIELDS(VALUE obj)
+{
+ RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
+
+ struct RObject *const ptr = ROBJECT(obj);
+
+ if (RB_UNLIKELY(RB_FL_ANY_RAW(obj, ROBJECT_HEAP))) {
+ return ptr->as.heap.fields;
+ }
+ else {
+ return ptr->as.ary;
+ }
+}
+
+#endif /* RBIMPL_ROBJECT_H */
diff --git a/include/ruby/internal/core/rregexp.h b/include/ruby/internal/core/rregexp.h
new file mode 100644
index 0000000000..cf54a399f1
--- /dev/null
+++ b/include/ruby/internal/core/rregexp.h
@@ -0,0 +1,168 @@
+#ifndef RBIMPL_RREGEXP_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RREGEXP_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RRegexp.
+ */
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/core/rstring.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RRegexp.
+ * @return The passed object casted to ::RRegexp.
+ */
+#define RREGEXP(obj) RBIMPL_CAST((struct RRegexp *)(obj))
+
+/**
+ * Convenient accessor macro.
+ *
+ * @param obj An object, which is in fact an ::RRegexp.
+ * @return The passed object's pattern buffer.
+ */
+#define RREGEXP_PTR(obj) (RREGEXP(obj)->ptr)
+/** @cond INTERNAL_MACRO */
+#define RREGEXP_SRC RREGEXP_SRC
+#define RREGEXP_SRC_PTR RREGEXP_SRC_PTR
+#define RREGEXP_SRC_LEN RREGEXP_SRC_LEN
+#define RREGEXP_SRC_END RREGEXP_SRC_END
+/** @endcond */
+
+struct re_patter_buffer; /* a.k.a. OnigRegexType, defined in onigmo.h */
+
+/**
+ * Ruby's regular expression. A regexp is compiled into its own intermediate
+ * representation. This one holds that info. Regexp "match" operation then
+ * executes that IR.
+ */
+struct RRegexp {
+
+ /** Basic part, including flags and class. */
+ struct RBasic basic;
+
+ /**
+ * The pattern buffer. This is a quasi-opaque struct that holds compiled
+ * intermediate representation of the regular expression.
+ *
+ * @note Compilation of a regexp could be delayed until actual match.
+ */
+ struct re_pattern_buffer *ptr;
+
+ /** Source code of this expression. */
+ const VALUE src;
+
+ /**
+ * Reference count. A regexp match can take extraordinarily long time to
+ * run. Ruby's regular expression is heavily extended and not a regular
+ * language any longer; runs in NP-time in practice. Now, Ruby also has
+ * threads and GVL. In order to prevent long GVL lockup, our regexp engine
+ * can release it on occasions. This means that multiple threads can touch
+ * a regular expressions at once. That itself is okay. But their cleanup
+ * phase shall wait for all the concurrent runs, to prevent use-after-free
+ * situation. This field is used to count such threads that are executing
+ * this particular pattern buffer.
+ *
+ * @warning Of course, touching this field from extension libraries causes
+ * catastrophic effects. Just leave it.
+ */
+ unsigned long usecnt;
+};
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Convenient getter function.
+ *
+ * @param[in] rexp The regular expression in question.
+ * @return The source code of the regular expression.
+ * @pre `rexp` must be of ::RRegexp.
+ */
+static inline VALUE
+RREGEXP_SRC(VALUE rexp)
+{
+ RBIMPL_ASSERT_TYPE(rexp, RUBY_T_REGEXP);
+ VALUE ret = RREGEXP(rexp)->src;
+ RBIMPL_ASSERT_TYPE(ret, RUBY_T_STRING);
+ return ret;
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Convenient getter function.
+ *
+ * @param[in] rexp The regular expression in question.
+ * @return The source code of the regular expression, in C's string.
+ * @pre `rexp` must be of ::RRegexp.
+ *
+ * @internal
+ *
+ * It seems nobody uses this function in the wild. Subject to hide?
+ */
+static inline char *
+RREGEXP_SRC_PTR(VALUE rexp)
+{
+ return RSTRING_PTR(RREGEXP_SRC(rexp));
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Convenient getter function.
+ *
+ * @param[in] rexp The regular expression in question.
+ * @return The length of the source code of the regular expression.
+ * @pre `rexp` must be of ::RRegexp.
+ *
+ * @internal
+ *
+ * It seems nobody uses this function in the wild. Subject to hide?
+ */
+static inline long
+RREGEXP_SRC_LEN(VALUE rexp)
+{
+ return RSTRING_LEN(RREGEXP_SRC(rexp));
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Convenient getter function.
+ *
+ * @param[in] rexp The regular expression in question.
+ * @return The end of the source code of the regular expression.
+ * @pre `rexp` must be of ::RRegexp.
+ *
+ * @internal
+ *
+ * It seems nobody uses this function in the wild. Subject to hide?
+ */
+static inline char *
+RREGEXP_SRC_END(VALUE rexp)
+{
+ return RSTRING_END(RREGEXP_SRC(rexp));
+}
+
+#endif /* RBIMPL_RREGEXP_H */
diff --git a/include/ruby/internal/core/rstring.h b/include/ruby/internal/core/rstring.h
new file mode 100644
index 0000000000..35175ea94a
--- /dev/null
+++ b/include/ruby/internal/core/rstring.h
@@ -0,0 +1,453 @@
+#ifndef RBIMPL_RSTRING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RSTRING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RString.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/arithmetic/long.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/value_type.h"
+#include "ruby/internal/warning_push.h"
+#include "ruby/assert.h"
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RString.
+ * @return The passed object casted to ::RString.
+ */
+#define RSTRING(obj) RBIMPL_CAST((struct RString *)(obj))
+
+/** @cond INTERNAL_MACRO */
+#define RSTRING_NOEMBED RSTRING_NOEMBED
+#define RSTRING_FSTR RSTRING_FSTR
+#define RSTRING_LEN RSTRING_LEN
+#define RSTRING_LENINT RSTRING_LENINT
+#define RSTRING_PTR RSTRING_PTR
+#define RSTRING_END RSTRING_END
+/** @endcond */
+
+/**
+ * @name Conversion of Ruby strings into C's
+ *
+ * @{
+ */
+
+/**
+ * Ensures that the parameter object is a String. This is done by calling its
+ * `to_str` method.
+ *
+ * @param[in,out] v Arbitrary Ruby object.
+ * @exception rb_eTypeError No implicit conversion defined.
+ * @post `v` is a String.
+ */
+#define StringValue(v) rb_string_value(&(v))
+
+/**
+ * Identical to #StringValue, except it returns a `char*`.
+ *
+ * @param[in,out] v Arbitrary Ruby object.
+ * @exception rb_eTypeError No implicit conversion defined.
+ * @return Converted Ruby string's backend C string.
+ * @post `v` is a String.
+ */
+#define StringValuePtr(v) rb_string_value_ptr(&(v))
+
+/**
+ * Identical to #StringValuePtr, except it additionally checks for the contents
+ * for viability as a C string. Ruby can accept wider range of contents as
+ * strings, compared to C. This function is to check that.
+ *
+ * @param[in,out] v Arbitrary Ruby object.
+ * @exception rb_eTypeError No implicit conversion defined.
+ * @exception rb_eArgError String is not C-compatible.
+ * @return Converted Ruby string's backend C string.
+ * @post `v` is a String.
+ */
+#define StringValueCStr(v) rb_string_value_cstr(&(v))
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define SafeStringValue(v) StringValue(v)
+
+/**
+ * Identical to #StringValue, except it additionally converts the string's
+ * encoding to default external encoding. Ruby has a concept called encodings.
+ * A string can have different encoding than the environment expects. Someone
+ * has to make sure its contents be converted to something suitable. This is
+ * that routine. Call it when necessary.
+ *
+ * @param[in,out] v Arbitrary Ruby object.
+ * @exception rb_eTypeError No implicit conversion defined.
+ * @return Converted Ruby string's backend C string.
+ * @post `v` is a String.
+ *
+ * @internal
+ *
+ * Not sure but it seems this macro does not raise on encoding
+ * incompatibilities? Doesn't sound right to @shyouhei.
+ */
+#define ExportStringValue(v) do { \
+ StringValue(v); \
+ (v) = rb_str_export(v); \
+} while (0)
+
+/** @} */
+
+/**
+ * @private
+ *
+ * Bits that you can set to ::RBasic::flags.
+ *
+ * @warning These enums are not the only bits we use for strings.
+ *
+ * @internal
+ *
+ * Actually all bits through FL_USER1 to FL_USER19 are used for strings. Why
+ * only this tiny part of them are made public here? @shyouhei can find no
+ * reason.
+ */
+enum ruby_rstring_flags {
+
+ /**
+ * This flag has something to do with memory footprint. If the string is
+ * short enough, ruby tries to be creative to abuse padding bits of struct
+ * ::RString for storing contents. If this flag is set that string does
+ * _not_ do that, to resort to good old fashioned external allocation
+ * strategy instead.
+ *
+ * @warning This bit has to be considered read-only. Setting/clearing
+ * this bit without corresponding fix up must cause immediate
+ * SEGV. Also, internal structures of a string change
+ * dynamically and transparently throughout of its lifetime.
+ * Don't assume it being persistent.
+ *
+ * @internal
+ *
+ * 3rd parties must not be aware that there even is more than one way to
+ * store a string. Might better be hidden.
+ */
+ RSTRING_NOEMBED = RUBY_FL_USER1,
+
+ /* Actually, string encodings are also encoded into the flags, using
+ * remaining bits.*/
+
+ /**
+ * This flag has something to do with infamous "f"string. What is a
+ * fstring? Well it is a special subkind of strings that is immutable,
+ * deduped globally, and managed by our GC. It is much like a Symbol (in
+ * fact Symbols are dynamic these days and are backended using fstrings).
+ * This concept has been silently introduced at some point in 2.x era.
+ * Since then it gained wider acceptance in the core. But extension
+ * libraries could not know that until very recently. Strings of this flag
+ * live in a special Limbo deep inside of the interpreter. Never try to
+ * manipulate it by hand.
+ *
+ * @internal
+ *
+ * Fstrings are not the only variant strings that we implement today.
+ * Other things are behind-the-scene. This is the only one that is visible
+ * from extension library. There is no clear reason why it has to be.
+ * Given there are more "polite" ways to create fstrings, it seems this bit
+ * need not be exposed to extension libraries. Might better be hidden.
+ */
+ RSTRING_FSTR = RUBY_FL_USER17
+};
+
+/**
+ * Ruby's String. A string in ruby conceptually has these information:
+ *
+ * - Encoding of the string.
+ * - Length of the string.
+ * - Contents of the string.
+ *
+ * It is worth noting that a string is _not_ an array of characters in ruby.
+ * It has never been. In 1.x a string was an array of integers. Since 2.x a
+ * string is no longer an array of anything. A string is a string -- just like
+ * a Time is not an integer.
+ */
+struct RString {
+
+ /** Basic part, including flags and class. */
+ struct RBasic basic;
+
+ /**
+ * Length of the string, not including terminating NUL character.
+ *
+ * @note This is in bytes.
+ */
+ long len;
+
+ /** String's specific fields. */
+ union {
+
+ /**
+ * Strings that use separated memory region for contents use this
+ * pattern.
+ */
+ struct {
+ /**
+ * Pointer to the contents of the string. In the old days each
+ * string had dedicated memory regions. That is no longer true
+ * today, but there still are strings of such properties. This
+ * field could be used to point such things.
+ */
+ char *ptr;
+
+ /** Auxiliary info. */
+ union {
+
+ /**
+ * Capacity of `*ptr`. A continuous memory region of at least
+ * `capa` bytes is expected to exist at `*ptr`. This can be
+ * bigger than `len`.
+ */
+ long capa;
+
+ /**
+ * Parent of the string. Nowadays strings can share their
+ * contents each other, constructing gigantic nest of objects.
+ * This situation is called "shared", and this is the field to
+ * control such properties.
+ */
+ VALUE shared;
+ } aux;
+ } heap;
+
+ /** Embedded contents. */
+ struct {
+ /* This is a length 1 array because:
+ * 1. GCC has a bug that does not optimize C flexible array members
+ * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452)
+ * 2. Zero length arrays are not supported by all compilers
+ */
+ char ary[1];
+ } embed;
+ } as;
+};
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * Identical to rb_check_string_type(), except it raises exceptions in case of
+ * conversion failures.
+ *
+ * @param[in] obj Target object.
+ * @exception rb_eTypeError No implicit conversion to String.
+ * @return Return value of `obj.to_str`.
+ * @see rb_io_get_io
+ * @see rb_ary_to_ary
+ */
+VALUE rb_str_to_str(VALUE obj);
+
+/**
+ * Identical to rb_str_to_str(), except it fills the passed pointer with the
+ * converted object.
+ *
+ * @param[in,out] ptr Pointer to a variable of target object.
+ * @exception rb_eTypeError No implicit conversion to String.
+ * @return Return value of `obj.to_str`.
+ * @post `*ptr` is the return value.
+ */
+VALUE rb_string_value(volatile VALUE *ptr);
+
+/**
+ * Identical to rb_str_to_str(), except it returns the converted string's
+ * backend memory region.
+ *
+ * @param[in,out] ptr Pointer to a variable of target object.
+ * @exception rb_eTypeError No implicit conversion to String.
+ * @post `*ptr` is the return value of `obj.to_str`.
+ * @return Pointer to the contents of the return value.
+ */
+char *rb_string_value_ptr(volatile VALUE *ptr);
+
+/**
+ * Identical to rb_string_value_ptr(), except it additionally checks for the
+ * contents for viability as a C string. Ruby can accept wider range of
+ * contents as strings, compared to C. This function is to check that.
+ *
+ * @param[in,out] ptr Pointer to a variable of target object.
+ * @exception rb_eTypeError No implicit conversion to String.
+ * @exception rb_eArgError String is not C-compatible.
+ * @post `*ptr` is the return value of `obj.to_str`.
+ * @return Pointer to the contents of the return value.
+ */
+char *rb_string_value_cstr(volatile VALUE *ptr);
+
+/**
+ * Identical to rb_str_to_str(), except it additionally converts the string
+ * into default external encoding. Ruby has a concept called encodings. A
+ * string can have different encoding than the environment expects. Someone
+ * has to make sure its contents be converted to something suitable. This is
+ * that routine. Call it when necessary.
+ *
+ * @param[in] obj Target object.
+ * @exception rb_eTypeError No implicit conversion to String.
+ * @return Converted ruby string of default external encoding.
+ */
+VALUE rb_str_export(VALUE obj);
+
+/**
+ * Identical to rb_str_export(), except it converts into the locale encoding
+ * instead.
+ *
+ * @param[in] obj Target object.
+ * @exception rb_eTypeError No implicit conversion to String.
+ * @return Converted ruby string of locale encoding.
+ */
+VALUE rb_str_export_locale(VALUE obj);
+
+RBIMPL_ATTR_ERROR(("rb_check_safe_str() and Check_SafeStr() are obsolete; use StringValue() instead"))
+/**
+ * @private
+ *
+ * @deprecated This function once was a thing in the old days, but makes no
+ * sense any longer today. Exists here for backwards
+ * compatibility only. You can safely forget about it.
+ */
+void rb_check_safe_str(VALUE);
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define Check_SafeStr(v) rb_check_safe_str(RBIMPL_CAST((VALUE)(v)))
+
+/**
+ * @private
+ *
+ * Prints diagnostic message to stderr when RSTRING_PTR or RSTRING_END
+ * is NULL.
+ *
+ * @param[in] func The function name where encountered NULL pointer.
+ */
+void rb_debug_rstring_null_ptr(const char *func);
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the length of the string.
+ *
+ * @param[in] str String in question.
+ * @return Its length, in bytes.
+ * @pre `str` must be an instance of ::RString.
+ */
+static inline long
+RSTRING_LEN(VALUE str)
+{
+ return RSTRING(str)->len;
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the contents pointer of the string.
+ *
+ * @param[in] str String in question.
+ * @return Pointer to its contents.
+ * @pre `str` must be an instance of ::RString.
+ */
+static inline char *
+RSTRING_PTR(VALUE str)
+{
+ char *ptr = RB_FL_TEST_RAW(str, RSTRING_NOEMBED) ?
+ RSTRING(str)->as.heap.ptr :
+ RSTRING(str)->as.embed.ary;
+
+ if (RUBY_DEBUG && RB_UNLIKELY(! ptr)) {
+ /* :BEWARE: @shyouhei thinks that currently, there are rooms for this
+ * function to return NULL. Better check here for maximum safety.
+ *
+ * Also, this is not rb_warn() because RSTRING_PTR() can be called
+ * during GC (see what obj_info() does). rb_warn() needs to allocate
+ * Ruby objects. That is not possible at this moment. */
+ rb_debug_rstring_null_ptr("RSTRING_PTR");
+ }
+
+ return ptr;
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the end of the contents pointer of the string.
+ *
+ * @param[in] str String in question.
+ * @return Pointer to its end of contents.
+ * @pre `str` must be an instance of ::RString.
+ */
+static inline char *
+RSTRING_END(VALUE str)
+{
+ char *ptr = RB_FL_TEST_RAW(str, RSTRING_NOEMBED) ?
+ RSTRING(str)->as.heap.ptr :
+ RSTRING(str)->as.embed.ary;
+ long len = RSTRING_LEN(str);
+
+ if (RUBY_DEBUG && RB_UNLIKELY(!ptr)) {
+ /* Ditto. */
+ rb_debug_rstring_null_ptr("RSTRING_END");
+ }
+
+ return &ptr[len];
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Identical to RSTRING_LEN(), except it differs for the return type.
+ *
+ * @param[in] str String in question.
+ * @exception rb_eRangeError Too long.
+ * @return Its length, in bytes.
+ * @pre `str` must be an instance of ::RString.
+ *
+ * @internal
+ *
+ * This API seems redundant but has actual usages.
+ */
+static inline int
+RSTRING_LENINT(VALUE str)
+{
+ return rb_long2int(RSTRING_LEN(str));
+}
+
+/**
+ * Convenient macro to obtain the contents and length at once.
+ *
+ * @param str String in question.
+ * @param ptrvar Variable where its contents is stored.
+ * @param lenvar Variable where its length is stored.
+ */
+# define RSTRING_GETMEM(str, ptrvar, lenvar) \
+ ((ptrvar) = RSTRING_PTR(str), \
+ (lenvar) = RSTRING_LEN(str))
+#endif /* RBIMPL_RSTRING_H */
diff --git a/include/ruby/internal/core/rstruct.h b/include/ruby/internal/core/rstruct.h
new file mode 100644
index 0000000000..0028a1bdcd
--- /dev/null
+++ b/include/ruby/internal/core/rstruct.h
@@ -0,0 +1,109 @@
+#ifndef RBIMPL_RSTRUCT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RSTRUCT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate struct RStruct.
+ * @note The struct RStruct itself is opaque.
+ */
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+#include "ruby/internal/arithmetic/long.h"
+#include "ruby/internal/arithmetic/int.h"
+#if !defined RUBY_EXPORT && !defined RUBY_NO_OLD_COMPATIBILITY
+# include "ruby/backward.h"
+#endif
+
+/** @cond INTERNAL_MACRO */
+#define RSTRUCT_LEN RSTRUCT_LEN
+#define RSTRUCT_SET RSTRUCT_SET
+#define RSTRUCT_GET RSTRUCT_GET
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * Returns the number of struct members.
+ *
+ * @param[in] st An instance of RStruct.
+ * @return The number of members of `st`.
+ * @pre `st` must be of ::RUBY_T_STRUCT.
+ */
+VALUE rb_struct_size(VALUE st);
+
+/**
+ * Resembles `Struct#[]`.
+ *
+ * @param[in] st An instance of RStruct.
+ * @param[in] k Index a.k.a. key of the struct.
+ * @exception rb_eTypeError `k` is neither Numeric, Symbol, nor String.
+ * @exception rb_eIndexError Numerical index out of range.
+ * @exception rb_eNameError No such key.
+ * @return The member stored at `k` in `st`.
+ * @pre `st` must be of ::RUBY_T_STRUCT.
+ */
+VALUE rb_struct_aref(VALUE st, VALUE k);
+
+/**
+ * Resembles `Struct#[]=`.
+ *
+ * @param[out] st An instance of RStruct.
+ * @param[in] k Index a.k.a. key of the struct.
+ * @param[in] v Value to store.
+ * @exception rb_eTypeError `k` is neither Numeric, Symbol, nor String.
+ * @exception rb_eIndexError Numerical index out of range.
+ * @exception rb_eNameError No such key.
+ * @return Passed `v`.
+ * @pre `st` must be of ::RUBY_T_STRUCT.
+ * @post `v` is stored at `k` in `st`.
+ */
+VALUE rb_struct_aset(VALUE st, VALUE k, VALUE v);
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_ARTIFICIAL()
+/** @copydoc rb_struct_size() */
+static inline long
+RSTRUCT_LEN(VALUE st)
+{
+ RBIMPL_ASSERT_TYPE(st, RUBY_T_STRUCT);
+
+ return RB_NUM2LONG(rb_struct_size(st));
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/** @copydoc rb_struct_aset() */
+static inline VALUE
+RSTRUCT_SET(VALUE st, int k, VALUE v)
+{
+ RBIMPL_ASSERT_TYPE(st, RUBY_T_STRUCT);
+
+ return rb_struct_aset(st, INT2NUM(k), (v));
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/** @copydoc rb_struct_aref() */
+static inline VALUE
+RSTRUCT_GET(VALUE st, int k)
+{
+ RBIMPL_ASSERT_TYPE(st, RUBY_T_STRUCT);
+
+ return rb_struct_aref(st, INT2NUM(k));
+}
+
+#endif /* RBIMPL_RSTRUCT_H */
diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h
new file mode 100644
index 0000000000..ec0794e387
--- /dev/null
+++ b/include/ruby/internal/core/rtypeddata.h
@@ -0,0 +1,761 @@
+#ifndef RBIMPL_RTYPEDDATA_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_RTYPEDDATA_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines struct ::RTypedData.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#include "ruby/internal/assume.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/flag_enum.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/core/rdata.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/error.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/static_assert.h"
+#include "ruby/internal/stdbool.h"
+#include "ruby/internal/value_type.h"
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define HAVE_TYPE_RB_DATA_TYPE_T 1
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define HAVE_RB_DATA_TYPE_T_FUNCTION 1
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define HAVE_RB_DATA_TYPE_T_PARENT 1
+
+/**
+ * This is a value you can set to ::rb_data_type_struct::dfree. Setting this
+ * means the data was allocated using ::ruby_xmalloc() (or variants), and shall
+ * be freed using ::ruby_xfree().
+ *
+ * @warning Do not use this if you want to use system malloc, because the
+ * system and Ruby might or might not share the same malloc
+ * implementation.
+ */
+#define RUBY_TYPED_DEFAULT_FREE RUBY_DEFAULT_FREE
+
+/**
+ * This is a value you can set to ::rb_data_type_struct::dfree. Setting this
+ * means the data is managed by someone else, like, statically allocated. Of
+ * course you are on your own then.
+ */
+#define RUBY_TYPED_NEVER_FREE RUBY_NEVER_FREE
+
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RTypedData.
+ * @return The passed object casted to ::RTypedData.
+ */
+#define RTYPEDDATA(obj) RBIMPL_CAST((struct RTypedData *)(obj))
+
+/**
+ * Convenient getter macro.
+ *
+ * @param v An object, which is in fact an ::RTypedData.
+ * @return The passed object's ::RTypedData::data field.
+ */
+#define RTYPEDDATA_DATA(v) (RTYPEDDATA(v)->data)
+
+/** @old{rb_check_typeddata} */
+#define Check_TypedStruct(v, t) \
+ rb_check_typeddata(RBIMPL_CAST((VALUE)(v)), (t))
+
+/** @cond INTERNAL_MACRO */
+#define RTYPEDDATA_P RTYPEDDATA_P
+#define RTYPEDDATA_TYPE RTYPEDDATA_TYPE
+#define TYPED_DATA_EMBEDDED ((VALUE)1)
+#define TYPED_DATA_PTR_MASK (~(TYPED_DATA_EMBEDDED))
+/** @endcond */
+
+/**
+ * Macros to see if each corresponding flag is defined.
+ */
+#define RUBY_TYPED_FREE_IMMEDIATELY RUBY_TYPED_FREE_IMMEDIATELY
+#define RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE
+#define RUBY_TYPED_WB_PROTECTED RUBY_TYPED_WB_PROTECTED
+#define RUBY_TYPED_PROMOTED1 RUBY_TYPED_PROMOTED1
+
+/**
+ * @private
+ *
+ * Bits for rb_data_type_struct::flags.
+ */
+enum
+RBIMPL_ATTR_FLAG_ENUM()
+rbimpl_typeddata_flags {
+ /**
+ * This flag has something to do with Ruby's global interpreter lock. For
+ * maximum safety, Ruby locks the entire VM during GC. However your
+ * callback functions could unintentionally unlock it, for instance when
+ * they try to flush an IO buffer. Such operations are dangerous (threads
+ * then run alongside of GC). By default, to prevent those scenario,
+ * callbacks are deferred until the GC engine is 100% sure threads can run.
+ * This flag skips that; structs with it are deallocated during the sweep
+ * phase.
+ *
+ * Using this flag needs deep understanding of both GC and threads. You
+ * would better leave it unspecified.
+ */
+ RUBY_TYPED_FREE_IMMEDIATELY = 1,
+
+ RUBY_TYPED_EMBEDDABLE = 2,
+
+ /**
+ * This flag has something to do with Ractor. Multiple Ractors run without
+ * protecting each other. Sharing an object among Ractors is basically
+ * dangerous, disabled by default. This flag is used to bypass that
+ * restriction. but setting it is not enough. In addition to do so, an
+ * object also has to be frozen, and be passed to
+ * rb_ractor_make_shareable() before being actually shareable. Of course,
+ * you have to manually prevent race conditions then.
+ *
+ * Using this flag needs deep understanding of multithreaded programming.
+ * You would better leave it unspecified.
+ */
+ RUBY_TYPED_FROZEN_SHAREABLE = RUBY_FL_SHAREABLE,
+
+ // experimental flag
+ // Similar to RUBY_TYPED_FROZEN_SHAREABLE, but doesn't make shareable
+ // reachable objects from this T_DATA object on the Ractor.make_shareable.
+ // If it refers to unshareable objects, simply raise an error.
+ // RUBY_TYPED_FROZEN_SHAREABLE_NO_REC = RUBY_FL_FINALIZE,
+
+ /**
+ * This flag has something to do with our garbage collector. These days
+ * ruby objects are "generational". There are those who are young and
+ * those who are old. Young objects are prone to die; monitored relatively
+ * extensively by the garbage collector. OTOH old objects tend to live
+ * longer. They are relatively rarely considered. This basically works.
+ * But there is one tweak that has to be exercised. When an elder object
+ * has reference(s) to younger one(s), that referenced objects must not
+ * die. In order to detect additions of such references, old generations
+ * are protected by write barriers. It is a very difficult hack to
+ * appropriately insert write barriers everywhere. This mechanism is
+ * disabled by default for 3rd party extensions (they never get aged). By
+ * specifying this flag you can enable the generational feature to your
+ * data structure. Of course, you have to manually insert write barriers
+ * then.
+ *
+ * Using this flag needs deep understanding of GC internals, often at the
+ * level of source code. You would better leave it unspecified.
+ */
+ RUBY_TYPED_WB_PROTECTED = RUBY_FL_WB_PROTECTED, /* THIS FLAG DEPENDS ON Ruby version */
+
+ /**
+ * This flag is used to distinguish RTypedData from deprecated RData objects.
+ */
+ RUBY_TYPED_FL_IS_TYPED_DATA = RUBY_FL_USERPRIV0,
+
+ /**
+ * This flag determines whether marking and compaction should be carried out
+ * using the dmark/dcompact callback functions or whether we should mark
+ * declaratively using a list of references defined inside the data struct we're wrapping
+ */
+ RUBY_TYPED_DECL_MARKING = RUBY_FL_USER2
+};
+
+/**
+ * This is the struct that holds necessary info for a struct. It roughly
+ * resembles a Ruby level class; multiple objects can share a ::rb_data_type_t
+ * instance.
+ */
+typedef struct rb_data_type_struct rb_data_type_t;
+
+/** @copydoc rb_data_type_t */
+struct rb_data_type_struct {
+
+ /**
+ * Name of structs of this kind. This is used for diagnostic purposes.
+ * This has to be unique in the process, but doesn't has to be a valid
+ * C/Ruby identifier.
+ */
+ const char *wrap_struct_name;
+
+ /** Function pointers. Resembles C++ `vtbl`.*/
+ struct {
+
+ /**
+ * This function is called when the object is experiencing GC marks.
+ * If it contains references to other Ruby objects, you need to mark
+ * them also. Otherwise GC will smash your data.
+ *
+ * @see rb_gc_mark()
+ * @warning This is called during GC runs. Object allocations are
+ * impossible at that moment (that is why GC runs).
+ */
+ RUBY_DATA_FUNC dmark;
+
+ /**
+ * This function is called when the object is no longer used. You need
+ * to do whatever necessary to avoid memory leaks.
+ *
+ * @warning This is called during GC runs. Object allocations are
+ * impossible at that moment (that is why GC runs).
+ */
+ RUBY_DATA_FUNC dfree;
+
+ /**
+ * This function is to query the size of the underlying memory regions.
+ *
+ * @internal
+ *
+ * This function has only one usage, which is form inside of
+ * `ext/objspace`.
+ */
+ size_t (*dsize)(const void *);
+
+ /**
+ * This function is called when the object is relocated. Like
+ * ::rb_data_type_struct::dmark, you need to update references to Ruby
+ * objects inside of your structs.
+ *
+ * @see rb_gc_location()
+ * @warning This is called during GC runs. Object allocations are
+ * impossible at that moment (that is why GC runs).
+ */
+ RUBY_DATA_FUNC dcompact;
+
+ /**
+ * @internal
+ */
+ void (*handle_weak_references)(void *);
+
+ /**
+ * This field is reserved for future extension. For now, it must be
+ * filled with zeros.
+ */
+ void *reserved[7]; /* For future extension.
+ This array *must* be filled with ZERO. */
+ } function;
+
+ /**
+ * Parent of this class. Sometimes C structs have inheritance-like
+ * relationships. An example is `struct sockaddr` and its family. If you
+ * design such things, make ::rb_data_type_t for each of them and connect
+ * using this field. Ruby can then transparently cast your data back and
+ * forth when you call #TypedData_Get_Struct().
+ *
+ * ```CXX
+ * struct parent { };
+ * static inline const rb_data_type_t parent_type = {
+ * .wrap_struct_name = "parent",
+ * };
+ *
+ * struct child: public parent { };
+ * static inline const rb_data_type_t child_type = {
+ * .wrap_struct_name = "child",
+ * .parent = &parent_type,
+ * };
+ *
+ * // This function can take both parent_class and child_class.
+ * static inline struct parent *
+ * get_parent(VALUE v)
+ * {
+ * struct parent *p;
+ * TypedData_Get_Struct(v, parent_type, struct parent, p);
+ * return p;
+ * }
+ * ```
+ */
+ const rb_data_type_t *parent;
+
+ /**
+ * Type-specific static data. This area can be used for any purpose by a
+ * programmer who define the type. Ruby does not manage this at all.
+ */
+ void *data; /* This area can be used for any purpose
+ by a programmer who define the type. */
+
+ /**
+ * Type-specific behavioural characteristics. This is a bitfield. It is
+ * an EXTREMELY WISE IDEA to leave this field blank. It is designed so
+ * that setting zero is the safest thing to do. If you risk to set any
+ * bits on, you have to know exactly what you are doing.
+ *
+ * @internal
+ *
+ * Why it has to be a ::VALUE? @shyouhei doesn't understand the design.
+ */
+ VALUE flags; /* RUBY_FL_WB_PROTECTED */
+};
+
+/**
+ * "Typed" user data. By using this, extension libraries can wrap a C struct
+ * to make it visible from Ruby. For instance if you have a `struct timeval`,
+ * and you want users to use it,
+ *
+ * ```CXX
+ * static inline const rb_data_type_t timeval_type = {
+ * // Note that unspecified fields are 0-filled by default.
+ * .wrap_struct_name = "timeval",
+ * .function = {
+ * .dmark = nullptr, // no need to mark
+ * .dfree = RUBY_TYPED_DEFAULT_FREE, // use ruby_xfree()
+ * .dsize = [](auto) {
+ * return sizeof(struct timeval);
+ * },
+ * },
+ * };
+ *
+ * extern "C" void
+ * Init_timeval(void)
+ * {
+ * auto klass = rb_define_class("YourName", rb_cObject);
+ *
+ * rb_define_alloc_func(klass, [](auto klass) {
+ * struct timeval *t;
+ * auto ret = TypedData_Make_Struct(
+ * klass, struct timeval, &timeval_type, t);
+ *
+ * if (auto i = gettimeofday(t, nullptr); i == -1) {
+ * rb_sys_fail("gettimeofday(3)");
+ * }
+ * else {
+ * return ret;
+ * }
+ * });
+ * }
+ * ```
+ */
+struct RTypedData {
+
+ /** The part that all ruby objects have in common. */
+ struct RBasic basic;
+
+ /** Direct reference to the slots that holds instance variables, if any **/
+ VALUE fields_obj;
+
+ /**
+ * This is a `const rb_data_type_t *const` value, with the low bits set:
+ *
+ * 1: Set if object is embedded.
+ *
+ * This field stores various information about how Ruby should handle a
+ * data. This roughly resembles a Ruby level class (apart from method
+ * definition etc.)
+ */
+ const VALUE type;
+
+ /** Pointer to the actual C level struct that you want to wrap. */
+ void *data;
+};
+
+#if !defined(__cplusplus) || __cplusplus >= 201103L
+RBIMPL_STATIC_ASSERT(data_in_rtypeddata, offsetof(struct RData, data) == offsetof(struct RTypedData, data));
+#endif
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_NONNULL((3))
+/**
+ * This is the primitive way to wrap an existing C struct into ::RTypedData.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] datap Pointer to the target C struct.
+ * @param[in] type The characteristics of the passed data.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return An allocated object that wraps `datap`.
+ */
+VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *type);
+
+RBIMPL_ATTR_NONNULL((3))
+/**
+ * Identical to rb_data_typed_object_wrap(), except it allocates a new data
+ * region internally instead of taking an existing one. The allocation is done
+ * using ruby_calloc(). Hence it makes no sense for `type->function.dfree` to
+ * be anything other than ::RUBY_TYPED_DEFAULT_FREE.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] size Requested size of memory to allocate.
+ * @param[in] type The characteristics of the passed data.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return An allocated object that wraps a new `size` byte region.
+ */
+VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Checks for the domestic relationship between the two.
+ *
+ * @param[in] child A data type supposed to be a child of `parent`.
+ * @param[in] parent A data type supposed to be a parent of `child`.
+ * @retval true `child` is a descendent of `parent`.
+ * @retval false Otherwise.
+ *
+ * @internal
+ *
+ * You can path NULL to both arguments, don't know what that means though.
+ */
+int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent);
+
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * Checks if the given object is of given kind.
+ *
+ * @param[in] obj An instance of ::RTypedData.
+ * @param[in] data_type Expected data type of `obj`.
+ * @retval true `obj` is of `data_type`.
+ * @retval false Otherwise.
+ */
+int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type);
+
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * Identical to rb_typeddata_is_kind_of(), except it raises exceptions instead
+ * of returning false.
+ *
+ * @param[in] obj An instance of ::RTypedData.
+ * @param[in] data_type Expected data type of `obj`.
+ * @exception rb_eTypeError obj is not of `data_type`.
+ * @return Unwrapped C struct that `obj` holds.
+ * @post Upon successful return `obj`'s type is guaranteed `data_type`.
+ */
+void *rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * @private
+ *
+ * Fails with the given object's type incompatibility to the type.
+ *
+ * This is an implementation detail of Check_Type. People don't use it
+ * directly.
+ *
+ * @param[in] obj The object in question.
+ * @param[in] expected Name of expected data type of `obj`.
+ */
+void rb_unexpected_object_type(VALUE obj, const char *expected);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * Fails with the given object's type incompatibility to the type.
+ *
+ * This is an implementation detail of #TypedData_Make_Struct. People don't
+ * use it directly.
+ *
+ * @param[in] actual Actual data type.
+ * @param[in] expected Expected data type.
+ */
+void rb_unexpected_typeddata(const rb_data_type_t *actual, const rb_data_type_t *expected);
+RBIMPL_SYMBOL_EXPORT_END()
+
+#if RUBY_DEBUG
+# define RBIMPL_TYPEDDATA_PRECONDITION(obj, unreachable) \
+ while (RB_UNLIKELY(!RB_TYPE_P(obj, RUBY_T_DATA))) { \
+ rb_unexpected_object_type(obj, "Data"); \
+ unreachable; \
+ }
+#else
+# define RBIMPL_TYPEDDATA_PRECONDITION(obj, unreachable) \
+ RBIMPL_ASSERT_NOTHING
+#endif
+
+/**
+ * Converts sval, a pointer to your struct, into a Ruby object.
+ *
+ * @param klass A ruby level class.
+ * @param data_type The type of `sval`.
+ * @param sval A pointer to your struct.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ */
+#define TypedData_Wrap_Struct(klass,data_type,sval)\
+ rb_data_typed_object_wrap((klass),(sval),(data_type))
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #TypedData_Make_Struct. People don't
+ * use it directly.
+ *
+ * @param result Variable name of created Ruby object.
+ * @param klass Ruby level class of the object.
+ * @param type Type name of the C struct.
+ * @param size Size of the C struct.
+ * @param data_type The data type describing `type`.
+ * @param sval Variable name of created C struct.
+ */
+#define TypedData_Make_Struct0(result, klass, type, size, data_type, sval) \
+ VALUE result = rb_data_typed_object_zalloc(klass, size, data_type); \
+ (sval) = RBIMPL_CAST((type *)rbimpl_typeddata_get_data(result)); \
+ RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval))
+
+/**
+ * Identical to #TypedData_Wrap_Struct, except it allocates a new data region
+ * internally instead of taking an existing one. The allocation is done using
+ * ruby_calloc().
+ *
+ * @param klass Ruby level class of the object.
+ * @param type Type name of the C struct.
+ * @param data_type The data type describing `type`.
+ * @param sval Variable name of created C struct.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ */
+#ifdef HAVE_STMT_AND_DECL_IN_EXPR
+#define TypedData_Make_Struct(klass, type, data_type, sval) \
+ RB_GNUC_EXTENSION({ \
+ TypedData_Make_Struct0( \
+ data_struct_obj, \
+ klass, \
+ type, \
+ sizeof(type), \
+ data_type, \
+ sval); \
+ data_struct_obj; \
+ })
+#else
+#define TypedData_Make_Struct(klass, type, data_type, sval) \
+ rb_data_typed_object_make( \
+ (klass), \
+ (data_type), \
+ RBIMPL_CAST((void **)&(sval)), \
+ sizeof(type))
+#endif
+
+static inline bool
+rbimpl_typeddata_embedded_p(VALUE obj)
+{
+ return (RTYPEDDATA(obj)->type) & TYPED_DATA_EMBEDDED;
+}
+
+RBIMPL_ATTR_DEPRECATED_INTERNAL_ONLY()
+static inline bool
+RTYPEDDATA_EMBEDDED_P(VALUE obj)
+{
+ RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(false));
+
+ return rbimpl_typeddata_embedded_p(obj);
+}
+
+static inline void *
+rbimpl_typeddata_get_data(VALUE obj)
+{
+ /* We reuse the data pointer in embedded TypedData. */
+ return rbimpl_typeddata_embedded_p(obj) ?
+ RBIMPL_CAST((void *)&RTYPEDDATA_DATA(obj)) :
+ RTYPEDDATA_DATA(obj);
+}
+
+static inline void *
+RTYPEDDATA_GET_DATA(VALUE obj)
+{
+ RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(NULL));
+
+ return rbimpl_typeddata_get_data(obj);
+}
+
+RBIMPL_ATTR_PURE()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * @private
+ *
+ * This is an implementation detail of Check_Type(). People don't use it
+ * directly.
+ *
+ * @param[in] obj Object in question
+ * @retval true `obj` is an instance of ::RTypedData.
+ * @retval false `obj` is an instance of ::RData.
+ * @pre `obj` must be a Ruby object of ::RUBY_T_DATA.
+ */
+static inline bool
+rbimpl_rtypeddata_p(VALUE obj)
+{
+ return FL_TEST_RAW(obj, RUBY_TYPED_FL_IS_TYPED_DATA);
+}
+
+RBIMPL_ATTR_PURE()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * @private
+ *
+ * Identical to rbimpl_rtypeddata_p(), except it is allowed to call on non-data
+ * objects.
+ *
+ * This is an implementation detail of inline functions defined in this file.
+ * People don't use it directly.
+ *
+ * @param[in] obj Object in question
+ * @retval true `obj` is an instance of ::RTypedData.
+ * @retval false `obj` is not an instance of ::RTypedData
+ */
+static inline bool
+rbimpl_obj_typeddata_p(VALUE obj)
+{
+ return RB_TYPE_P(obj, RUBY_T_DATA) && rbimpl_rtypeddata_p(obj);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks whether the passed object is ::RTypedData or ::RData.
+ *
+ * @param[in] obj Object in question
+ * @retval true `obj` is an instance of ::RTypedData.
+ * @retval false `obj` is an instance of ::RData.
+ * @pre `obj` must be a Ruby object of ::RUBY_T_DATA.
+ */
+static inline bool
+RTYPEDDATA_P(VALUE obj)
+{
+ RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(false));
+
+ return rbimpl_rtypeddata_p(obj);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_RETURNS_NONNULL()
+/**
+ * Queries for the type of given object.
+ *
+ * @param[in] obj Object in question
+ * @return Data type struct that corresponds to `obj`.
+ * @pre `obj` must be an instance of ::RTypedData.
+ */
+static inline const rb_data_type_t *
+RTYPEDDATA_TYPE(VALUE obj)
+{
+ RBIMPL_TYPEDDATA_PRECONDITION(obj, RBIMPL_UNREACHABLE_RETURN(NULL));
+
+ VALUE type = RTYPEDDATA(obj)->type & TYPED_DATA_PTR_MASK;
+ const rb_data_type_t *ptr = RBIMPL_CAST((const rb_data_type_t *)type);
+ RBIMPL_ASSERT_OR_ASSUME(ptr);
+ return ptr;
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NONNULL(())
+static inline bool
+rbimpl_typeddata_inherited_p_inline(const rb_data_type_t *child, const rb_data_type_t *parent)
+{
+ do {
+ if (RB_LIKELY(child == parent)) return true;
+ } while ((child = child->parent) != NULL);
+ return false;
+}
+#define rb_typeddata_inherited_p rbimpl_typeddata_inherited_p_inline
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NONNULL((2))
+static inline bool
+rbimpl_typeddata_is_kind_of_inline(VALUE obj, const rb_data_type_t *data_type)
+{
+ if (RB_UNLIKELY(!rbimpl_obj_typeddata_p(obj))) return false;
+ return rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type);
+}
+#define rb_typeddata_is_kind_of rbimpl_typeddata_is_kind_of_inline
+
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * @private
+ *
+ * This is an implementation detail of TypedData_Get_Struct(). Don't use it
+ * directly.
+ */
+static inline void *
+rbimpl_check_typeddata(VALUE obj, const rb_data_type_t *expected_type)
+{
+ if (RB_UNLIKELY(!rbimpl_obj_typeddata_p(obj))) {
+ rb_unexpected_object_type(obj, expected_type->wrap_struct_name);
+ }
+
+ const rb_data_type_t *actual_type = RTYPEDDATA_TYPE(obj);
+ if (RB_UNLIKELY(!rb_typeddata_inherited_p(actual_type, expected_type))){
+ rb_unexpected_typeddata(actual_type, expected_type);
+ }
+
+ return RTYPEDDATA_GET_DATA(obj);
+}
+
+
+/**
+ * Obtains a C struct from inside of a wrapper Ruby object.
+ *
+ * @param obj An instance of ::RTypedData.
+ * @param type Type name of the C struct.
+ * @param data_type The data type describing `type`.
+ * @param sval Variable name of obtained C struct.
+ * @exception rb_eTypeError `obj` is not a kind of `data_type`.
+ * @return Unwrapped C struct that `obj` holds.
+ */
+#define TypedData_Get_Struct(obj,type,data_type,sval) \
+ ((sval) = RBIMPL_CAST((type *)rbimpl_check_typeddata((obj), (data_type))))
+
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * While we don't stop you from using this function, it seems to be an
+ * implementation detail of #TypedData_Make_Struct, which is preferred over
+ * this one.
+ *
+ * @param[in] klass Ruby level class of the returning object.
+ * @param[in] type The data type
+ * @param[out] datap Return pointer.
+ * @param[in] size Size of the C struct.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eNoMemError Out of memory.
+ * @return A created Ruby object.
+ * @post `*datap` points to the C struct wrapped by the returned object.
+ */
+static inline VALUE
+rb_data_typed_object_make(VALUE klass, const rb_data_type_t *type, void **datap, size_t size)
+{
+ TypedData_Make_Struct0(result, klass, void, size, type, *datap);
+ return result;
+}
+
+#endif /* RBIMPL_RTYPEDDATA_H */
diff --git a/include/ruby/internal/ctype.h b/include/ruby/internal/ctype.h
new file mode 100644
index 0000000000..8b24026311
--- /dev/null
+++ b/include/ruby/internal/ctype.h
@@ -0,0 +1,545 @@
+#ifndef RBIMPL_CTYPE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_CTYPE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Our own, locale independent, character handling routines.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <ctype.h>
+#endif
+
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+
+/**
+ * @name Old character classification macros
+ *
+ * What is this #ISPRINT business? Well, according to our VCS and some
+ * internet surfing, it appears that the initial intent of these macros were to
+ * mimic codes appear in common in several GNU projects. As far as @shyouhei
+ * detects they seem to originate GNU regex (that standalone one rather than
+ * Gnulib or Glibc), and at least date back to 1995.
+ *
+ * Let me lawfully quote from a GNU coreutils commit
+ * https://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=49803907f5dbd7646184a8912c9db9b09dcd0f22
+ *
+ * > Jim Meyering writes:
+ * >
+ * > "... Some ctype macros are valid only for character codes that
+ * > isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
+ * > using /bin/cc or gcc but without giving an ansi option). So, all
+ * > ctype uses should be through macros like ISPRINT... If
+ * > STDC_HEADERS is defined, then autoconf has verified that the ctype
+ * > macros don't need to be guarded with references to isascii. ...
+ * > Defining isascii to 1 should let any compiler worth its salt
+ * > eliminate the && through constant folding."
+ * >
+ * > Bruno Haible adds:
+ * >
+ * > "... Furthermore, isupper(c) etc. have an undefined result if c is
+ * > outside the range -1 <= c <= 255. One is tempted to write isupper(c)
+ * > with c being of type `char', but this is wrong if c is an 8-bit
+ * > character >= 128 which gets sign-extended to a negative value.
+ * > The macro ISUPPER protects against this as well."
+ *
+ * So the intent was to reroute old problematic systems that no longer exist.
+ * At the same time the problems described above no longer hurt us, because we
+ * decided to completely avoid using system-provided isupper etc. to reinvent
+ * the wheel. These macros are entirely legacy; please ignore them.
+ *
+ * But let me also put stress that GNU people are wise; they use those macros
+ * only inside of their own implementations and never let them be public. On
+ * the other hand ruby has thoughtlessly publicised them to 3rd party libraries
+ * since its beginning, which is a very bad idea. These macros are too easy to
+ * get conflicted with definitions elsewhere.
+ *
+ * New programs should stick to the `rb_` prefixed names.
+ *
+ * @note It seems we just mimic the API. We do not share their implementation
+ * with GPL-ed programs.
+ *
+ * @{
+ */
+#ifndef ISPRINT
+# define ISASCII rb_isascii /**< @old{rb_isascii}*/
+# define ISPRINT rb_isprint /**< @old{rb_isprint}*/
+# define ISGRAPH rb_isgraph /**< @old{rb_isgraph}*/
+# define ISSPACE rb_isspace /**< @old{rb_isspace}*/
+# define ISUPPER rb_isupper /**< @old{rb_isupper}*/
+# define ISLOWER rb_islower /**< @old{rb_islower}*/
+# define ISALNUM rb_isalnum /**< @old{rb_isalnum}*/
+# define ISALPHA rb_isalpha /**< @old{rb_isalpha}*/
+# define ISDIGIT rb_isdigit /**< @old{rb_isdigit}*/
+# define ISXDIGIT rb_isxdigit /**< @old{rb_isxdigit}*/
+# define ISBLANK rb_isblank /**< @old{rb_isblank}*/
+# define ISCNTRL rb_iscntrl /**< @old{rb_iscntrl}*/
+# define ISPUNCT rb_ispunct /**< @old{rb_ispunct}*/
+#endif
+
+#define TOUPPER rb_toupper /**< @old{rb_toupper}*/
+#define TOLOWER rb_tolower /**< @old{rb_tolower}*/
+#define STRCASECMP st_locale_insensitive_strcasecmp /**< @old{st_locale_insensitive_strcasecmp}*/
+#define STRNCASECMP st_locale_insensitive_strncasecmp /**< @old{st_locale_insensitive_strncasecmp}*/
+#define STRTOUL ruby_strtoul /**< @old{ruby_strtoul}*/
+
+/** @} */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/** @name locale insensitive functions
+ * @{
+ */
+
+/* In descriptions below, `the POSIX Locale` and `the "C" locale` are tactfully
+ * used as to whether the described function mimics POSIX or C99. */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Our own locale-insensitive version of `strcasecmp(3)`. The "case" here
+ * always means that of the POSIX Locale. It doesn't depend on runtime locale
+ * settings.
+ *
+ * @param[in] s1 Comparison LHS.
+ * @param[in] s2 Comparison RHS.
+ * @retval -1 `s1` is "less" than `s2`.
+ * @retval 0 Both strings converted into lowercase would be identical.
+ * @retval 1 `s1` is "greater" than `s2`.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ */
+int st_locale_insensitive_strcasecmp(const char *s1, const char *s2);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Our own locale-insensitive version of `strcnasecmp(3)`. The "case" here
+ * always means that of the POSIX Locale. It doesn't depend on runtime locale
+ * settings.
+ *
+ * @param[in] s1 Comparison LHS.
+ * @param[in] s2 Comparison RHS.
+ * @param[in] n Comparison shall stop after first `n` bytes are scanned.
+ * @retval -1 `s1` is "less" than `s2`.
+ * @retval 0 Both strings converted into lowercase would be identical.
+ * @retval 1 `s1` is "greater" than `s2`.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning This function is _not_ timing safe.
+ */
+int st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Our own locale-insensitive version of `strtoul(3)`. The conversion is done
+ * as if the current locale is set to the "C" locale, no matter actual runtime
+ * locale settings.
+ *
+ * @note This is needed because `strtoul("i", 0, 36)` would return zero
+ * if it is locale sensitive and the current locale is `tr_TR`.
+ * @param[in] str String of digits, optionally preceded with whitespaces
+ * (ignored) and optionally `+` or `-` sign.
+ * @param[out] endptr NULL, or an arbitrary pointer (overwritten on return).
+ * @param[in] base `2` to `36` inclusive for each base, or special case
+ * `0` to detect the base from the contents of the string.
+ * @return Converted integer, casted to unsigned long.
+ * @post If `endptr` is not NULL, it is updated to point the first such
+ * byte where conversion failed.
+ * @note This function sets `errno` on failure.
+ * - `EINVAL`: Passed `base` is out of range.
+ * - `ERANGE`: Converted integer is out of range of `long`.
+ * @warning As far as @shyouhei reads ISO/IEC 9899:2018 section 7.22.1.4, a
+ * conforming `strtoul` implementation shall render `ERANGE`
+ * whenever it finds the input string represents a negative
+ * integer. Such thing can never be representable using `unsigned
+ * long`. However this implementation does not honour that
+ * language. It just casts such negative value to the return
+ * type, resulting a very big return value. This behaviour is at
+ * least questionable. But we can no longer change that at this
+ * point.
+ * @note Not only does this function works under the "C" locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ */
+unsigned long ruby_strtoul(const char *str, char **endptr, int base);
+RBIMPL_SYMBOL_EXPORT_END()
+
+/*
+ * We are making the functions below to return `int` instead of `bool`. They
+ * have been as such since their birth at 5f237d79033b2109afb768bc889611fa9630.
+ */
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isascii(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval false `c` is out of range of ASCII character set.
+ * @retval true Yes it is.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isascii(int c)
+{
+ return '\0' <= c && c <= '\x7f';
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isupper(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in IEEE 1003.1 section 7.3.1.1 "upper".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isupper(int c)
+{
+ return 'A' <= c && c <= 'Z';
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `islower(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in IEEE 1003.1 section 7.3.1.1 "lower".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_islower(int c)
+{
+ return 'a' <= c && c <= 'z';
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isalpha(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in either IEEE 1003.1 section 7.3.1.1
+ * "upper" or "lower".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isalpha(int c)
+{
+ return rb_isupper(c) || rb_islower(c);
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isdigit(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in IEEE 1003.1 section 7.3.1.1 "digit".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isdigit(int c)
+{
+ return '0' <= c && c <= '9';
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isalnum(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in either IEEE 1003.1 section 7.3.1.1
+ * "upper", "lower", or "digit".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isalnum(int c)
+{
+ return rb_isalpha(c) || rb_isdigit(c);
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isxdigit(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in IEEE 1003.1 section 7.3.1.1 "xdigit".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isxdigit(int c)
+{
+ return rb_isdigit(c) || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f');
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isblank(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in IEEE 1003.1 section 7.3.1.1 "blank".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isblank(int c)
+{
+ return c == ' ' || c == '\t';
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isspace(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in IEEE 1003.1 section 7.3.1.1 "space".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isspace(int c)
+{
+ return c == ' ' || ('\t' <= c && c <= '\r');
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `iscntrl(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in IEEE 1003.1 section 7.3.1.1 "cntrl".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_iscntrl(int c)
+{
+ return ('\0' <= c && c < ' ') || c == '\x7f';
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Identical to rb_isgraph(), except it also returns true for `' '`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in either IEEE 1003.1 section 7.3.1.1
+ * "upper", "lower", "digit", "punct", or a `' '`.
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isprint(int c)
+{
+ return ' ' <= c && c <= '\x7e';
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `ispunct(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in IEEE 1003.1 section 7.3.1.1 "punct".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_ispunct(int c)
+{
+ return !rb_isalnum(c);
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `isgraph(3)`.
+ *
+ * @param[in] c Byte in question to query.
+ * @retval true `c` is listed in either IEEE 1003.1 section 7.3.1.1
+ * "upper", "lower", "digit", or "punct".
+ * @retval false Anything else.
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_isgraph(int c)
+{
+ return '!' <= c && c <= '\x7e';
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `tolower(3)`.
+ *
+ * @param[in] c Byte in question to convert.
+ * @retval c The byte is not listed in IEEE 1003.1 section 7.3.1.1
+ * "upper".
+ * @retval otherwise Byte converted using the map defined in IEEE 1003.1
+ * section 7.3.1 "tolower".
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_tolower(int c)
+{
+ return rb_isupper(c) ? (c|0x20) : c;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Our own locale-insensitive version of `toupper(3)`.
+ *
+ * @param[in] c Byte in question to convert.
+ * @retval c The byte is not listed in in IEEE 1003.1 section
+ * 7.3.1.1 "lower".
+ * @retval otherwise Byte converted using the map defined in IEEE 1003.1
+ * section 7.3.1 "toupper".
+ * @note Not only does this function works under the POSIX Locale, but
+ * also assumes its execution character set be what ruby calls an
+ * ASCII-compatible character set; which does not include for
+ * instance EBCDIC or UTF-16LE.
+ * @warning `c` is an int. This means that when you pass a `char` value
+ * here, it experiences "integer promotion" as defined in ISO/IEC
+ * 9899:2018 section 6.3.1.1 paragraph 1.
+ */
+static inline int
+rb_toupper(int c)
+{
+ return rb_islower(c) ? (c&0x5f) : c;
+}
+
+/** @} */
+#endif /* RBIMPL_CTYPE_H */
diff --git a/include/ruby/internal/dllexport.h b/include/ruby/internal/dllexport.h
new file mode 100644
index 0000000000..71026e7100
--- /dev/null
+++ b/include/ruby/internal/dllexport.h
@@ -0,0 +1,80 @@
+#ifndef RBIMPL_DLLEXPORT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_DLLEXPORT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Tweaking visibility of C variables/functions.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/compiler_is.h"
+
+/**
+ * Declaration of externally visible global variables. Here "externally" means
+ * they should be visible from extension libraries. Depending on operating
+ * systems (dynamic linkers, to be precise), global variables inside of a DLL
+ * may or may not be visible form outside of that DLL by default. This
+ * declaration manually tweaks that default and ensures the declared variable
+ * be truly globally visible.
+ *
+ * ```CXX
+ * extern VALUE foo; // hidden on some OS
+ * RUBY_EXTERN VALUE foo; // ensure visible
+ * ```
+ */
+#undef RUBY_EXTERN
+#if defined(RUBY_EXPORT)
+# define RUBY_EXTERN extern
+#elif defined(_WIN32)
+# define RUBY_EXTERN extern __declspec(dllimport)
+#else
+# define RUBY_EXTERN extern
+#endif
+
+#ifndef RUBY_SYMBOL_EXPORT_BEGIN
+# define RUBY_SYMBOL_EXPORT_BEGIN /* begin */
+#endif
+
+#ifndef RUBY_SYMBOL_EXPORT_END
+# define RUBY_SYMBOL_EXPORT_END /* end */
+#endif
+
+#ifndef RUBY_FUNC_EXPORTED
+# define RUBY_FUNC_EXPORTED /* void */
+#endif
+
+/** @endcond */
+
+/** Shortcut macro equivalent to `RUBY_SYMBOL_EXPORT_BEGIN extern "C" {`.
+ * \@shyouhei finds it handy. */
+#if defined(__DOXYGEN__)
+# define RBIMPL_SYMBOL_EXPORT_BEGIN() /* void */
+#elif defined(__cplusplus)
+# define RBIMPL_SYMBOL_EXPORT_BEGIN() RUBY_SYMBOL_EXPORT_BEGIN extern "C" {
+#else
+# define RBIMPL_SYMBOL_EXPORT_BEGIN() RUBY_SYMBOL_EXPORT_BEGIN
+#endif
+
+/** Counterpart of #RBIMPL_SYMBOL_EXPORT_BEGIN */
+#if defined(__DOXYGEN__)
+# define RBIMPL_SYMBOL_EXPORT_END() /* void */
+#elif defined(__cplusplus)
+# define RBIMPL_SYMBOL_EXPORT_END() } RUBY_SYMBOL_EXPORT_END
+#else
+# define RBIMPL_SYMBOL_EXPORT_END() RUBY_SYMBOL_EXPORT_END
+#endif
+#endif /* RBIMPL_DLLEXPORT_H */
diff --git a/include/ruby/internal/dosish.h b/include/ruby/internal/dosish.h
new file mode 100644
index 0000000000..7d354ddd1a
--- /dev/null
+++ b/include/ruby/internal/dosish.h
@@ -0,0 +1,89 @@
+#ifndef RBIMPL_DOSISH_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_DOSISH_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Support for so-called dosish systems.
+ */
+#ifdef __CYGWIN__
+#undef _WIN32
+#endif
+
+#if defined(_WIN32)
+/*
+ DOSISH mean MS-Windows style filesystem.
+ But you should use more precise macros like DOSISH_DRIVE_LETTER, PATH_SEP,
+ ENV_IGNORECASE or CASEFOLD_FILESYSTEM.
+ */
+#define DOSISH 1
+# define DOSISH_DRIVE_LETTER
+#endif
+
+#ifdef _WIN32
+#include "ruby/win32.h"
+#endif
+
+/** The delimiter of `PATH` environment variable. */
+#if defined(DOSISH)
+#define PATH_SEP ";"
+#else
+#define PATH_SEP ":"
+#endif
+
+/** Identical to #PATH_SEP, except it is of type `char`. */
+#define PATH_SEP_CHAR PATH_SEP[0]
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ *
+ * @internal
+ *
+ * For historical interests: there was an operating system called Human68k
+ * which used an environment variable called `"path"` for this purpose.
+ */
+#define PATH_ENV "PATH"
+
+#if defined(DOSISH)
+#define ENV_IGNORECASE
+#endif
+
+/**
+ * Stone age assumption was that an operating system supports only one file
+ * system at a moment. This macro was to detect if such (one and only) file
+ * system has case sensitivity. This assumption is largely not true any
+ * longer; most operating systems can mount many kinds of file systems side by
+ * side. Also there are file systems that do or do not ignore cases depending
+ * on configuration (e.g. EXT4's `casefold` feature).
+ *
+ * This macro is still used internally (for instance Ruby level constant
+ * `File::FNM_SYSCASE` depends on it), but it is basically a wrong idea for you
+ * to use it today. Please just find another way.
+ */
+#ifndef CASEFOLD_FILESYSTEM
+# if defined DOSISH
+# define CASEFOLD_FILESYSTEM 1
+# else
+# define CASEFOLD_FILESYSTEM 0
+# endif
+#endif
+
+#endif /* RBIMPL_DOSISH_H */
diff --git a/include/ruby/internal/encoding/coderange.h b/include/ruby/internal/encoding/coderange.h
new file mode 100644
index 0000000000..c89f871518
--- /dev/null
+++ b/include/ruby/internal/encoding/coderange.h
@@ -0,0 +1,202 @@
+#ifndef RUBY_INTERNAL_ENCODING_CODERANGE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_CODERANGE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines for code ranges.
+ */
+
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/** What rb_enc_str_coderange() returns. */
+enum ruby_coderange_type {
+
+ /** The object's coderange is unclear yet. */
+ RUBY_ENC_CODERANGE_UNKNOWN = 0,
+
+ /** The object holds 0 to 127 inclusive and nothing else. */
+ RUBY_ENC_CODERANGE_7BIT = ((int)RUBY_FL_USER8),
+
+ /** The object's encoding and contents are consistent each other */
+ RUBY_ENC_CODERANGE_VALID = ((int)RUBY_FL_USER9),
+
+ /** The object holds invalid/malformed/broken character(s). */
+ RUBY_ENC_CODERANGE_BROKEN = ((int)(RUBY_FL_USER8|RUBY_FL_USER9)),
+
+ /** Where the coderange resides. */
+ RUBY_ENC_CODERANGE_MASK = (RUBY_ENC_CODERANGE_7BIT|
+ RUBY_ENC_CODERANGE_VALID|
+ RUBY_ENC_CODERANGE_BROKEN)
+};
+
+RBIMPL_ATTR_CONST()
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_ENC_CODERANGE_CLEAN_P. People don't
+ * use it directly.
+ *
+ * @param[in] cr An enum ::ruby_coderange_type.
+ * @retval 1 It is.
+ * @retval 0 It isn't.
+ */
+static inline int
+rb_enc_coderange_clean_p(int cr)
+{
+ return (cr ^ (cr >> 1)) & RUBY_ENC_CODERANGE_7BIT;
+}
+
+RBIMPL_ATTR_CONST()
+/**
+ * Queries if a code range is "clean". "Clean" in this context means it is
+ * known and valid.
+ *
+ * @param[in] cr An enum ::ruby_coderange_type.
+ * @retval 1 It is.
+ * @retval 0 It isn't.
+ */
+static inline bool
+RB_ENC_CODERANGE_CLEAN_P(enum ruby_coderange_type cr)
+{
+ return rb_enc_coderange_clean_p(RBIMPL_CAST((int)cr));
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/**
+ * Queries the (inline) code range of the passed object. The object must be
+ * capable of having inline encoding. Using this macro needs deep
+ * understanding of bit level object binary layout.
+ *
+ * @param[in] obj Target object.
+ * @return An enum ::ruby_coderange_type.
+ */
+static inline enum ruby_coderange_type
+RB_ENC_CODERANGE(VALUE obj)
+{
+ VALUE ret = RB_FL_TEST_RAW(obj, RUBY_ENC_CODERANGE_MASK);
+
+ return RBIMPL_CAST((enum ruby_coderange_type)ret);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/**
+ * Queries the (inline) code range of the passed object is
+ * ::RUBY_ENC_CODERANGE_7BIT. The object must be capable of having inline
+ * encoding. Using this macro needs deep understanding of bit level object
+ * binary layout.
+ *
+ * @param[in] obj Target object.
+ * @retval 1 It is ascii only.
+ * @retval 0 Otherwise (including cases when the range is not known).
+ */
+static inline bool
+RB_ENC_CODERANGE_ASCIIONLY(VALUE obj)
+{
+ return RB_ENC_CODERANGE(obj) == RUBY_ENC_CODERANGE_7BIT;
+}
+
+/**
+ * Destructively modifies the passed object so that its (inline) code range is
+ * the passed one. The object must be capable of having inline encoding.
+ * Using this macro needs deep understanding of bit level object binary layout.
+ *
+ * @param[out] obj Target object.
+ * @param[out] cr An enum ::ruby_coderange_type.
+ * @post `obj`'s code range is `cr`.
+ */
+static inline void
+RB_ENC_CODERANGE_SET(VALUE obj, enum ruby_coderange_type cr)
+{
+ RB_FL_UNSET_RAW(obj, RUBY_ENC_CODERANGE_MASK);
+ RB_FL_SET_RAW(obj, cr);
+}
+
+/**
+ * Destructively clears the passed object's (inline) code range. The object
+ * must be capable of having inline encoding. Using this macro needs deep
+ * understanding of bit level object binary layout.
+ *
+ * @param[out] obj Target object.
+ * @post `obj`'s code range is ::RUBY_ENC_CODERANGE_UNKNOWN.
+ */
+static inline void
+RB_ENC_CODERANGE_CLEAR(VALUE obj)
+{
+ RB_FL_UNSET_RAW(obj, RUBY_ENC_CODERANGE_MASK);
+}
+
+RBIMPL_ATTR_CONST()
+/* assumed ASCII compatibility */
+/**
+ * "Mix" two code ranges into one. This is handy for instance when you
+ * concatenate two strings into one. Consider one of then is valid but the
+ * other isn't. The result must be invalid. This macro computes that kind of
+ * mixture.
+ *
+ * @param[in] a An enum ::ruby_coderange_type.
+ * @param[in] b Another enum ::ruby_coderange_type.
+ * @return The `a` "and" `b`.
+ */
+static inline enum ruby_coderange_type
+RB_ENC_CODERANGE_AND(enum ruby_coderange_type a, enum ruby_coderange_type b)
+{
+ if (a == RUBY_ENC_CODERANGE_7BIT) {
+ return b;
+ }
+ else if (a != RUBY_ENC_CODERANGE_VALID) {
+ return RUBY_ENC_CODERANGE_UNKNOWN;
+ }
+ else if (b == RUBY_ENC_CODERANGE_7BIT) {
+ return RUBY_ENC_CODERANGE_VALID;
+ }
+ else {
+ return b;
+ }
+}
+
+#define ENC_CODERANGE_MASK RUBY_ENC_CODERANGE_MASK /**< @old{RUBY_ENC_CODERANGE_MASK} */
+#define ENC_CODERANGE_UNKNOWN RUBY_ENC_CODERANGE_UNKNOWN /**< @old{RUBY_ENC_CODERANGE_UNKNOWN} */
+#define ENC_CODERANGE_7BIT RUBY_ENC_CODERANGE_7BIT /**< @old{RUBY_ENC_CODERANGE_7BIT} */
+#define ENC_CODERANGE_VALID RUBY_ENC_CODERANGE_VALID /**< @old{RUBY_ENC_CODERANGE_VALID} */
+#define ENC_CODERANGE_BROKEN RUBY_ENC_CODERANGE_BROKEN /**< @old{RUBY_ENC_CODERANGE_BROKEN} */
+#define ENC_CODERANGE_CLEAN_P(cr) RB_ENC_CODERANGE_CLEAN_P(cr) /**< @old{RB_ENC_CODERANGE_CLEAN_P} */
+#define ENC_CODERANGE(obj) RB_ENC_CODERANGE(obj) /**< @old{RB_ENC_CODERANGE} */
+#define ENC_CODERANGE_ASCIIONLY(obj) RB_ENC_CODERANGE_ASCIIONLY(obj) /**< @old{RB_ENC_CODERANGE_ASCIIONLY} */
+#define ENC_CODERANGE_SET(obj,cr) RB_ENC_CODERANGE_SET(obj,cr) /**< @old{RB_ENC_CODERANGE_SET} */
+#define ENC_CODERANGE_CLEAR(obj) RB_ENC_CODERANGE_CLEAR(obj) /**< @old{RB_ENC_CODERANGE_CLEAR} */
+#define ENC_CODERANGE_AND(a, b) RB_ENC_CODERANGE_AND(a, b) /**< @old{RB_ENC_CODERANGE_AND} */
+#define ENCODING_CODERANGE_SET(obj, encindex, cr) RB_ENCODING_CODERANGE_SET(obj, encindex, cr) /**< @old{RB_ENCODING_CODERANGE_SET} */
+
+/** @cond INTERNAL_MACRO */
+#define RB_ENC_CODERANGE RB_ENC_CODERANGE
+#define RB_ENC_CODERANGE_AND RB_ENC_CODERANGE_AND
+#define RB_ENC_CODERANGE_ASCIIONLY RB_ENC_CODERANGE_ASCIIONLY
+#define RB_ENC_CODERANGE_CLEAN_P RB_ENC_CODERANGE_CLEAN_P
+#define RB_ENC_CODERANGE_CLEAR RB_ENC_CODERANGE_CLEAR
+#define RB_ENC_CODERANGE_SET RB_ENC_CODERANGE_SET
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_INTERNAL_ENCODING_CODERANGE_H */
diff --git a/include/ruby/internal/encoding/ctype.h b/include/ruby/internal/encoding/ctype.h
new file mode 100644
index 0000000000..05c314aeb3
--- /dev/null
+++ b/include/ruby/internal/encoding/ctype.h
@@ -0,0 +1,258 @@
+#ifndef RUBY_INTERNAL_ENCODING_CTYPE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_CTYPE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to query chacater types.
+ */
+
+#include "ruby/onigmo.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/encoding/encoding.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Queries if the passed pointer points to a newline character. What is a
+ * newline and what is not depends on the passed encoding.
+ *
+ * @param[in] p Pointer to a possibly-middle of a character.
+ * @param[in] end End of the string.
+ * @param[in] enc Encoding.
+ * @retval false It isn't.
+ * @retval true It is.
+ */
+static inline bool
+rb_enc_is_newline(const char *p, const char *e, rb_encoding *enc)
+{
+ OnigUChar *up = RBIMPL_CAST((OnigUChar *)p);
+ OnigUChar *ue = RBIMPL_CAST((OnigUChar *)e);
+
+ return ONIGENC_IS_MBC_NEWLINE(enc, up, ue);
+}
+
+/**
+ * Queries if the passed code point is of passed character type in the passed
+ * encoding. The "character type" here is a set of macros defined in onigmo.h,
+ * like `ONIGENC_CTYPE_PUNCT`.
+ *
+ * @param[in] c An `OnigCodePoint` value.
+ * @param[in] t An `OnigCtype` value.
+ * @param[in] enc A `rb_encoding*` value.
+ * @retval true `c` is of `t` in `enc`.
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_isctype(OnigCodePoint c, OnigCtype t, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_CTYPE(enc, c, t);
+}
+
+/**
+ * Identical to rb_isascii(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval false `c` is out of range of ASCII character set in `enc`.
+ * @retval true Otherwise.
+ *
+ * @internal
+ *
+ * `enc` is ignored. This is at least an intentional implementation detail
+ * (not a bug). But there could be rooms for future extensions.
+ */
+static inline bool
+rb_enc_isascii(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_ASCII(c);
+}
+
+/**
+ * Identical to rb_isalpha(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "ALPHA".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_isalpha(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_ALPHA(enc, c);
+}
+
+/**
+ * Identical to rb_islower(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "LOWER".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_islower(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_LOWER(enc, c);
+}
+
+/**
+ * Identical to rb_isupper(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "UPPER".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_isupper(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_UPPER(enc, c);
+}
+
+/**
+ * Identical to rb_iscntrl(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "CNTRL".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_iscntrl(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_CNTRL(enc, c);
+}
+
+/**
+ * Identical to rb_ispunct(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "PUNCT".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_ispunct(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_PUNCT(enc, c);
+}
+
+/**
+ * Identical to rb_isalnum(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "ANUM".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_isalnum(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_ALNUM(enc, c);
+}
+
+/**
+ * Identical to rb_isprint(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "PRINT".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_isprint(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_PRINT(enc, c);
+}
+
+/**
+ * Identical to rb_isspace(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "PRINT".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_isspace(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_SPACE(enc, c);
+}
+
+/**
+ * Identical to rb_isdigit(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @retval true `enc` classifies `c` as "DIGIT".
+ * @retval false Otherwise.
+ */
+static inline bool
+rb_enc_isdigit(OnigCodePoint c, rb_encoding *enc)
+{
+ return ONIGENC_IS_CODE_DIGIT(enc, c);
+}
+
+RBIMPL_ATTR_CONST()
+/**
+ * Identical to rb_toupper(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @return `c`'s (Ruby's definition of) upper case counterpart.
+ *
+ * @internal
+ *
+ * As `RBIMPL_ATTR_CONST` implies this function ignores `enc`.
+ */
+int rb_enc_toupper(int c, rb_encoding *enc);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Identical to rb_tolower(), except it additionally takes an encoding.
+ *
+ * @param[in] c A code point.
+ * @param[in] enc An encoding.
+ * @return `c`'s (Ruby's definition of) lower case counterpart.
+ *
+ * @internal
+ *
+ * As `RBIMPL_ATTR_CONST` implies this function ignores `enc`.
+ */
+int rb_enc_tolower(int c, rb_encoding *enc);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/** @cond INTERNAL_MACRO */
+#define rb_enc_is_newline rb_enc_is_newline
+#define rb_enc_isalnum rb_enc_isalnum
+#define rb_enc_isalpha rb_enc_isalpha
+#define rb_enc_isascii rb_enc_isascii
+#define rb_enc_isctype rb_enc_isctype
+#define rb_enc_isdigit rb_enc_isdigit
+#define rb_enc_islower rb_enc_islower
+#define rb_enc_isprint rb_enc_isprint
+#define rb_enc_iscntrl rb_enc_iscntrl
+#define rb_enc_ispunct rb_enc_ispunct
+#define rb_enc_isspace rb_enc_isspace
+#define rb_enc_isupper rb_enc_isupper
+/** @endcond */
+
+#endif /* RUBY_INTERNAL_ENCODING_CTYPE_H */
diff --git a/include/ruby/internal/encoding/encoding.h b/include/ruby/internal/encoding/encoding.h
new file mode 100644
index 0000000000..a58f9f2b15
--- /dev/null
+++ b/include/ruby/internal/encoding/encoding.h
@@ -0,0 +1,1044 @@
+#ifndef RUBY_INTERNAL_ENCODING_ENCODING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_ENCODING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines ::rb_encoding
+ */
+
+#include "ruby/oniguruma.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/attr/returns_nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/encoding/coderange.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/fl_type.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * `Encoding` class.
+ *
+ * @ingroup object
+ */
+RUBY_EXTERN VALUE rb_cEncoding;
+
+/**
+ * @private
+ *
+ * Bit constants used when embedding encodings into ::RBasic::flags. Extension
+ * libraries must not bother such things.
+ */
+enum ruby_encoding_consts {
+
+ /** Max possible number of embeddable encodings. */
+ RUBY_ENCODING_INLINE_MAX = 127,
+
+ /** Where inline encodings reside. */
+ RUBY_ENCODING_SHIFT = (RUBY_FL_USHIFT+10),
+
+ /** Bits we use to store inline encodings. */
+ RUBY_ENCODING_MASK = (RUBY_ENCODING_INLINE_MAX<<RUBY_ENCODING_SHIFT
+ /* RUBY_FL_USER10..RUBY_FL_USER16 */),
+
+ /** Max possible length of an encoding name. */
+ RUBY_ENCODING_MAXNAMELEN = 42
+};
+
+#define ENCODING_INLINE_MAX RUBY_ENCODING_INLINE_MAX /**< @old{RUBY_ENCODING_INLINE_MAX} */
+#define ENCODING_SHIFT RUBY_ENCODING_SHIFT /**< @old{RUBY_ENCODING_SHIFT} */
+#define ENCODING_MASK RUBY_ENCODING_MASK /**< @old{RUBY_ENCODING_MASK} */
+
+/**
+ * Destructively assigns the passed encoding to the passed object. The object
+ * must be capable of having inline encoding. Using this macro needs deep
+ * understanding of bit level object binary layout.
+ *
+ * @param[out] obj Target object to modify.
+ * @param[in] ecindex Encoding in encindex format.
+ * @post `obj`'s encoding is `encindex`.
+ */
+static inline void
+RB_ENCODING_SET_INLINED(VALUE obj, int encindex)
+{
+ VALUE f = /* upcast */ RBIMPL_CAST((VALUE)encindex);
+
+ f <<= RUBY_ENCODING_SHIFT;
+ RB_FL_UNSET_RAW(obj, RUBY_ENCODING_MASK);
+ RB_FL_SET_RAW(obj, f);
+}
+
+/**
+ * Queries the encoding of the passed object. The encoding must be smaller
+ * than ::RUBY_ENCODING_INLINE_MAX, which means you have some assumption on the
+ * return value. This means the API is for internal use only.
+ *
+ * @param[in] obj Target object.
+ * @return `obj`'s encoding index.
+ */
+static inline int
+RB_ENCODING_GET_INLINED(VALUE obj)
+{
+ VALUE ret = RB_FL_TEST_RAW(obj, RUBY_ENCODING_MASK) >> RUBY_ENCODING_SHIFT;
+
+ return RBIMPL_CAST((int)ret);
+}
+
+#define ENCODING_SET_INLINED(obj,i) RB_ENCODING_SET_INLINED(obj,i) /**< @old{RB_ENCODING_SET_INLINED} */
+#define ENCODING_SET(obj,i) RB_ENCODING_SET(obj,i) /**< @old{RB_ENCODING_SET} */
+#define ENCODING_GET_INLINED(obj) RB_ENCODING_GET_INLINED(obj) /**< @old{RB_ENCODING_GET_INLINED} */
+#define ENCODING_GET(obj) RB_ENCODING_GET(obj) /**< @old{RB_ENCODING_GET} */
+#define ENCODING_IS_ASCII8BIT(obj) RB_ENCODING_IS_ASCII8BIT(obj) /**< @old{RB_ENCODING_IS_ASCII8BIT} */
+#define ENCODING_MAXNAMELEN RUBY_ENCODING_MAXNAMELEN /**< @old{RUBY_ENCODING_MAXNAMELEN} */
+
+/**
+ * The type of encoding. Our design here is we take Oniguruma/Onigmo's
+ * multilingualisation schema as our base data structure.
+ */
+typedef const OnigEncodingType rb_encoding;
+
+RBIMPL_ATTR_NOALIAS()
+/**
+ * Converts a character option to its encoding. It only supports a very
+ * limited set of Japanese encodings due to its Japanese origin. Ruby still
+ * has this in-core for backwards compatibility. But new codes must not bother
+ * such concept like one-character encoding option. Consider deprecated in
+ * practice.
+ *
+ * @param[in] c One of `['n', 'e', 's', 'u', 'i', 'x', 'm']`.
+ * @param[out] option Return buffer.
+ * @param[out] kcode Return buffer.
+ * @retval 1 `c` understood properly.
+ * @retval 0 `c` is not understood.
+ * @post `option` is a ::OnigOptionType.
+ * @post `kcode` is an enum `ruby_preserved_encindex`.
+ *
+ * @internal
+ *
+ * `kcode` is opaque because `ruby_preserved_encindex` is not visible from
+ * extension libraries. But who cares?
+ */
+int rb_char_to_option_kcode(int c, int *option, int *kcode);
+
+/**
+ * Creates a new "dummy" encoding. Roughly speaking, an encoding is dummy when
+ * it is stateful. Notable example of dummy encoding are those defined in
+ * ISO/IEC 2022
+ *
+ * @param[in] name Name of the creating encoding.
+ * @exception rb_eArgError Duplicated or malformed `name`.
+ * @return New dummy encoding's index.
+ * @post Encoding named `name` is created, whose index is the return
+ * value.
+ */
+int rb_define_dummy_encoding(const char *name);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries if the passed encoding is dummy.
+ *
+ * @param[in] enc Encoding in question.
+ * @retval 1 It is.
+ * @retval 0 It isn't.
+ */
+int rb_enc_dummy_p(rb_encoding *enc);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries the index of the encoding. An encoding's index is a Ruby-local
+ * concept. It is a (sequential) number assigned to each encoding.
+ *
+ * @param[in] enc Encoding in question.
+ * @return Its index.
+ * @note You can pass null pointers to this function. It is equivalent
+ * to rb_usascii_encindex() then.
+ */
+int rb_enc_to_index(rb_encoding *enc);
+
+/**
+ * Queries the index of the encoding of the passed object, if any.
+ *
+ * @param[in] obj Object in question.
+ * @retval -1 `obj` is incapable of having an encoding.
+ * @retval otherwise `obj`'s encoding's index.
+ */
+int rb_enc_get_index(VALUE obj);
+
+/**
+ * @alias{rb_enc_get_index}
+ *
+ * @internal
+ *
+ * Implementation wise this is not a verbatim alias of rb_enc_get_index(). But
+ * the API is consistent. Don't bother.
+ */
+static inline int
+RB_ENCODING_GET(VALUE obj)
+{
+ int encindex = RB_ENCODING_GET_INLINED(obj);
+
+ if (encindex == RUBY_ENCODING_INLINE_MAX) {
+ return rb_enc_get_index(obj);
+ }
+ else {
+ return encindex;
+ }
+}
+
+/**
+ * Destructively assigns an encoding (via its index) to an object.
+ *
+ * @param[out] obj Object in question.
+ * @param[in] encindex An encoding index.
+ * @exception rb_eFrozenError `obj` is frozen.
+ * @exception rb_eArgError `obj` is incapable of having an encoding.
+ * @exception rb_eEncodingError `encindex` is out of bounds.
+ * @exception rb_eLoadError Failed to load the encoding.
+ */
+void rb_enc_set_index(VALUE obj, int encindex);
+
+/** @alias{rb_enc_set_index} */
+static inline void
+RB_ENCODING_SET(VALUE obj, int encindex)
+{
+ rb_enc_set_index(obj, encindex);
+}
+
+/**
+ * This is #RB_ENCODING_SET + RB_ENC_CODERANGE_SET combo. The object must be
+ * capable of having inline encoding. Using this macro needs deep
+ * understanding of bit level object binary layout.
+ *
+ * @param[out] obj Target object.
+ * @param[in] encindex Encoding in encindex format.
+ * @param[in] cr An enum ::ruby_coderange_type.
+ * @post `obj`'s encoding is `encindex`.
+ * @post `obj`'s code range is `cr`.
+ */
+static inline void
+RB_ENCODING_CODERANGE_SET(VALUE obj, int encindex, enum ruby_coderange_type cr)
+{
+ RB_ENCODING_SET(obj, encindex);
+ RB_ENC_CODERANGE_SET(obj, cr);
+}
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries if the passed object can have its encoding.
+ *
+ * @param[in] obj Object in question.
+ * @retval 1 It can.
+ * @retval 0 It cannot.
+ */
+int rb_enc_capable(VALUE obj);
+
+/**
+ * Queries the index of the encoding.
+ *
+ * @param[in] name Name of the encoding to find.
+ * @exception rb_eArgError No such encoding named `name`.
+ * @retval -1 `name` exists, but unable to load.
+ * @retval otherwise Index of encoding named `name`.
+ */
+int rb_enc_find_index(const char *name);
+
+/**
+ * Registers an "alias" name. In the wild, an encoding can be called using
+ * multiple names. For instance an encoding known as `"CP932"` is also called
+ * `"SJIS"` on occasions. This API registers such relationships.
+ *
+ * @param[in] alias New name.
+ * @param[in] orig Old name.
+ * @exception rb_eArgError `alias` is duplicated or malformed.
+ * @retval -1 Failed to load `orig`.
+ * @retval otherwise The index of `orig` and `alias`.
+ * @post `alias` is a synonym of `orig`. They refer to the identical
+ * encoding.
+ */
+int rb_enc_alias(const char *alias, const char *orig);
+
+/**
+ * Obtains a encoding index from a wider range of objects (than
+ * rb_enc_find_index()).
+ *
+ * @param[in] obj An ::rb_cEncoding, or its name in ::rb_cString.
+ * @retval -1 `obj` is unexpected type/contents.
+ * @retval otherwise Index corresponding to `obj`.
+ */
+int rb_to_encoding_index(VALUE obj);
+
+/**
+ * Identical to rb_find_encoding(), except it raises an exception instead of
+ * returning NULL.
+ *
+ * @param[in] obj An ::rb_cEncoding, or its name in ::rb_cString.
+ * @exception rb_eTypeError `obj` is neither ::rb_cEncoding nor ::rb_cString.
+ * @exception rb_eArgError `obj` is an unknown encoding name.
+ * @return Encoding of `obj`.
+ */
+rb_encoding *rb_to_encoding(VALUE obj);
+
+/**
+ * Identical to rb_to_encoding_index(), except the return type.
+ *
+ * @param[in] obj An ::rb_cEncoding, or its name in ::rb_cString.
+ * @exception rb_eTypeError `obj` is neither ::rb_cEncoding nor ::rb_cString.
+ * @retval NULL No such encoding.
+ * @return otherwise Encoding of `obj`.
+ */
+rb_encoding *rb_find_encoding(VALUE obj);
+
+/**
+ * Identical to rb_enc_get_index(), except the return type.
+ *
+ * @param[in] obj Object in question.
+ * @retval NULL Obj is incapable of having an encoding.
+ * @retval otherwise `obj`'s encoding.
+ */
+rb_encoding *rb_enc_get(VALUE obj);
+
+/**
+ * Look for the "common" encoding between the two. One character can or cannot
+ * be expressed depending on an encoding. This function finds the super-set of
+ * encodings that satisfy contents of both arguments. If that is impossible
+ * returns NULL.
+ *
+ * @param[in] str1 An object.
+ * @param[in] str2 Another object.
+ * @retval NULL No encoding can satisfy both at once.
+ * @retval otherwise Common encoding between the two.
+ * @note Arguments can be non-string, e.g. Regexp.
+ */
+rb_encoding *rb_enc_compatible(VALUE str1, VALUE str2);
+
+/**
+ * Identical to rb_enc_compatible(), except it raises an exception instead of
+ * returning NULL.
+ *
+ * @param[in] str1 An object.
+ * @param[in] str2 Another object.
+ * @exception rb_eEncCompatError No encoding can satisfy both.
+ * @return Common encoding between the two.
+ * @note Arguments can be non-string, e.g. Regexp.
+ */
+rb_encoding *rb_enc_check(VALUE str1,VALUE str2);
+
+/**
+ * Identical to rb_enc_set_index(), except it additionally does contents fix-up
+ * depending on the passed object. It for instance changes the byte length of
+ * terminating `U+0000` according to the passed encoding.
+ *
+ * @param[out] obj Object in question.
+ * @param[in] encindex An encoding index.
+ * @exception rb_eFrozenError `obj` is frozen.
+ * @exception rb_eArgError `obj` is incapable of having an encoding.
+ * @exception rb_eEncodingError `encindex` is out of bounds.
+ * @exception rb_eLoadError Failed to load the encoding.
+ * @return The passed `obj`.
+ * @post `obj`'s contents might be fixed according to `encindex`.
+ */
+VALUE rb_enc_associate_index(VALUE obj, int encindex);
+
+/**
+ * Identical to rb_enc_associate_index(), except it takes an encoding itself
+ * instead of its index.
+ *
+ * @param[out] obj Object in question.
+ * @param[in] enc An encoding.
+ * @exception rb_eFrozenError `obj` is frozen.
+ * @exception rb_eArgError `obj` is incapable of having an encoding.
+ * @return The passed `obj`.
+ * @post `obj`'s contents might be fixed according to `enc`.
+ */
+VALUE rb_enc_associate(VALUE obj, rb_encoding *enc);
+
+/**
+ * Destructively copies the encoding of the latter object to that of former
+ * one. It can also be seen as a routine identical to
+ * rb_enc_associate_index(), except it takes an object's encoding instead of an
+ * encoding's index.
+ *
+ * @param[out] dst Object to modify.
+ * @param[in] src Object to reference.
+ * @exception rb_eFrozenError `dst` is frozen.
+ * @exception rb_eArgError `dst` is incapable of having an encoding.
+ * @exception rb_eEncodingError `src` is incapable of having an encoding.
+ * @post `dst`'s encoding is that of `src`'s.
+ */
+void rb_enc_copy(VALUE dst, VALUE src);
+
+
+/**
+ * Identical to rb_find_encoding(), except it takes an encoding index instead
+ * of a Ruby object.
+ *
+ * @param[in] idx An encoding index.
+ * @retval NULL No such encoding.
+ * @retval otherwise An encoding whose index is `idx`.
+ */
+rb_encoding *rb_enc_from_index(int idx);
+
+/**
+ * Identical to rb_find_encoding(), except it takes a C's string instead of
+ * Ruby's.
+ *
+ * @param[in] name Name of the encoding to query.
+ * @retval NULL No such encoding.
+ * @retval otherwise An encoding whose index is `idx`.
+ */
+rb_encoding *rb_enc_find(const char *name);
+
+/**
+ * Queries the (canonical) name of the passed encoding.
+ *
+ * @param[in] enc An encoding.
+ * @return Its name.
+ */
+static inline const char *
+rb_enc_name(rb_encoding *enc)
+{
+ return enc->name;
+}
+
+/**
+ * Queries the minimum number of bytes that the passed encoding needs to
+ * represent a character. For ASCII and compatible encodings this is typically
+ * 1. There are however encodings whose minimum is not 1; they are
+ * historically called wide characters.
+ *
+ * @param[in] enc An encoding.
+ * @return Its least possible number of bytes except 0.
+ */
+static inline int
+rb_enc_mbminlen(rb_encoding *enc)
+{
+ return enc->min_enc_len;
+}
+
+/**
+ * Queries the maximum number of bytes that the passed encoding needs to
+ * represent a character. Fixed-width encodings have the same value for this
+ * one and #rb_enc_mbminlen. However there are variable-width encodings.
+ * UTF-8, for instance, takes from 1 up to 6 bytes.
+ *
+ * @param[in] enc An encoding.
+ * @return Its maximum possible number of bytes of a character.
+ */
+static inline int
+rb_enc_mbmaxlen(rb_encoding *enc)
+{
+ return enc->max_enc_len;
+}
+
+/**
+ * Queries the number of bytes of the character at the passed pointer.
+ *
+ * @param[in] p Pointer to a character's first byte.
+ * @param[in] e End of the string that has `p`.
+ * @param[in] enc Encoding of the string.
+ * @return If the character at `p` does not end until `e`, number of bytes
+ * between `p` and `e`. Otherwise the number of bytes that the
+ * character at `p` is encoded.
+ *
+ * @internal
+ *
+ * Strictly speaking there are chances when `p` points to a middle byte of a
+ * wide character. This function returns "the number of bytes from `p` to
+ * nearest of either `e` or the next character boundary", if you go strict.
+ */
+int rb_enc_mbclen(const char *p, const char *e, rb_encoding *enc);
+
+/**
+ * Identical to rb_enc_mbclen() unless the character at `p` overruns `e`. That
+ * can happen for instance when you read from a socket and its partial read
+ * cuts a wide character in-between. In those situations this function
+ * "estimates" theoretical length of the character in question. Typically it
+ * tends to be possible to know how many bytes a character needs before
+ * actually reaching its end; for instance UTF-8 encodes a character's length
+ * in the first byte of it. This function returns that info.
+ *
+ * @note This implies that the string is not broken.
+ *
+ * @param[in] p Pointer to the character's first byte.
+ * @param[in] e End of the string that has `p`.
+ * @param[in] enc Encoding of the string.
+ * @return Number of bytes of character at `p`, measured or estimated.
+ */
+int rb_enc_fast_mbclen(const char *p, const char *e, rb_encoding *enc);
+
+/**
+ * Queries the number of bytes of the character at the passed pointer. This
+ * function returns 3 different types of information:
+ *
+ * ```CXX
+ * auto n = rb_enc_precise_mbclen(p, q, r);
+ *
+ * if (ONIGENC_MBCLEN_CHARFOUND_P(n)) {
+ * // Character found. Normal return.
+ * auto found_length = ONIGENC_MBCLEN_CHARFOUND_LEN(n);
+ * }
+ * else if (ONIGENC_MBCLEN_NEEDMORE_P(n)) {
+ * // Character overruns past `q`; needs more.
+ * auto requested_length = ONIGENC_MBCLEN_NEEDMORE_LEN(n);
+ * }
+ * else {
+ * // `p` is broken.
+ * assert(ONIGENC_MBCLEN_INVALID_P(n));
+ * }
+ * ```
+ *
+ * @param[in] p Pointer to the character's first byte.
+ * @param[in] e End of the string that has `p`.
+ * @param[in] enc Encoding of the string.
+ * @return Encoded read/needed number of bytes (see above).
+ */
+int rb_enc_precise_mbclen(const char *p, const char *e, rb_encoding *enc);
+
+#define MBCLEN_CHARFOUND_P(ret) ONIGENC_MBCLEN_CHARFOUND_P(ret) /**< @old{ONIGENC_MBCLEN_CHARFOUND_P} */
+#define MBCLEN_CHARFOUND_LEN(ret) ONIGENC_MBCLEN_CHARFOUND_LEN(ret) /**< @old{ONIGENC_MBCLEN_CHARFOUND_LEN} */
+#define MBCLEN_INVALID_P(ret) ONIGENC_MBCLEN_INVALID_P(ret) /**< @old{ONIGENC_MBCLEN_INVALID_P} */
+#define MBCLEN_NEEDMORE_P(ret) ONIGENC_MBCLEN_NEEDMORE_P(ret) /**< @old{ONIGENC_MBCLEN_NEEDMORE_P} */
+#define MBCLEN_NEEDMORE_LEN(ret) ONIGENC_MBCLEN_NEEDMORE_LEN(ret) /**< @old{ONIGENC_MBCLEN_NEEDMORE_LEN} */
+
+/**
+ * Queries the code point of character pointed by the passed pointer. If that
+ * code point is included in ASCII that code point is returned. Otherwise -1.
+ * This can be different from just looking at the first byte. For instance it
+ * reads 2 bytes in case of UTF-16BE.
+ *
+ * @param[in] p Pointer to the character's first byte.
+ * @param[in] e End of the string that has `p`.
+ * @param[in] len Return buffer.
+ * @param[in] enc Encoding of the string.
+ * @retval -1 The character at `p` is not i ASCII.
+ * @retval otherwise A code point of the character at `p`.
+ * @post `len` (if set) is the number of bytes of `p`.
+ */
+int rb_enc_ascget(const char *p, const char *e, int *len, rb_encoding *enc);
+
+/**
+ * Queries the code point of character pointed by the passed pointer.
+ * Exceptions happen in case of broken input.
+ *
+ * @param[in] p Pointer to the character's first byte.
+ * @param[in] e End of the string that has `p`.
+ * @param[in] len Return buffer.
+ * @param[in] enc Encoding of the string.
+ * @exception rb_eArgError `p` is broken.
+ * @return Code point of the character pointed by `p`.
+ * @post `len` (if set) is the number of bytes of `p`.
+ */
+unsigned int rb_enc_codepoint_len(const char *p, const char *e, int *len, rb_encoding *enc);
+
+/**
+ * Queries the code point of character pointed by the passed pointer.
+ * Exceptions happen in case of broken input.
+ *
+ * @deprecated Use rb_enc_codepoint_len() instead.
+ * @param[in] p Pointer to the character's first byte.
+ * @param[in] e End of the string that has `p`.
+ * @param[in] enc Encoding of the string.
+ * @exception rb_eArgError `p` is broken.
+ * @return Code point of the character pointed by `p`.
+ *
+ * @internal
+ *
+ * @matz says in commit 91e5ba1cb865a2385d3e1cbfacd824496898e098 that the line
+ * below is a "prototype for obsolete function". However even today there
+ * still are some use cases of it throughout our repository. It seems it has
+ * its own niche.
+ */
+static inline unsigned int
+rb_enc_codepoint(const char *p, const char *e, rb_encoding *enc)
+{
+ return rb_enc_codepoint_len(p, e, 0, enc);
+ /* ^^^
+ * This can be `NULL` in C, `nullptr` in C++, and `0` for both.
+ * We choose the most portable one here.
+ */
+}
+
+
+/**
+ * Identical to rb_enc_codepoint(), except it assumes the passed character is
+ * not broken.
+ *
+ * @param[in] p Pointer to the character's first byte.
+ * @param[in] e End of the string that has `p`.
+ * @param[in] enc Encoding of the string.
+ * @return Code point of the character pointed by `p`.
+ */
+static inline OnigCodePoint
+rb_enc_mbc_to_codepoint(const char *p, const char *e, rb_encoding *enc)
+{
+ const OnigUChar *up = RBIMPL_CAST((const OnigUChar *)p);
+ const OnigUChar *ue = RBIMPL_CAST((const OnigUChar *)e);
+
+ return ONIGENC_MBC_TO_CODE(enc, up, ue);
+}
+
+/**
+ * Queries the number of bytes requested to represent the passed code point
+ * using the passed encoding.
+ *
+ * @param[in] code Code point in question.
+ * @param[in] enc Encoding to convert the code into a byte sequence.
+ * @exception rb_eArgError `enc` does not glean `code`.
+ * @return Number of bytes requested to represent `code` using `enc`.
+ */
+int rb_enc_codelen(int code, rb_encoding *enc);
+
+/**
+ * Identical to rb_enc_codelen(), except it returns 0 for invalid code points.
+ *
+ * @param[in] c Code point in question.
+ * @param[in] enc Encoding to convert `c` into a byte sequence.
+ * @retval 0 `c` is invalid.
+ * @return otherwise Number of bytes needed for `enc` to encode `c`.
+ */
+static inline int
+rb_enc_code_to_mbclen(int c, rb_encoding *enc)
+{
+ OnigCodePoint uc = RBIMPL_CAST((OnigCodePoint)c);
+
+ return ONIGENC_CODE_TO_MBCLEN(enc, uc);
+}
+
+/**
+ * Identical to rb_enc_uint_chr(), except it writes back to the passed buffer
+ * instead of allocating one.
+ *
+ * @param[in] c Code point.
+ * @param[out] buf Return buffer.
+ * @param[in] enc Target encoding scheme.
+ * @retval <= 0 `c` is invalid in `enc`.
+ * @return otherwise Number of bytes written to `buf`.
+ * @post `c` is encoded according to `enc`, then written to `buf`.
+ *
+ * @internal
+ *
+ * The second argument must be typed. But its current usages prevent us from
+ * being any stricter than this. :FIXME:
+ */
+static inline int
+rb_enc_mbcput(unsigned int c, void *buf, rb_encoding *enc)
+{
+ OnigCodePoint uc = RBIMPL_CAST((OnigCodePoint)c);
+ OnigUChar *ubuf = RBIMPL_CAST((OnigUChar *)buf);
+
+ return ONIGENC_CODE_TO_MBC(enc, uc, ubuf);
+}
+
+/**
+ * Queries the previous (left) character.
+ *
+ * @param[in] s Start of the string.
+ * @param[in] p Pointer to a character.
+ * @param[in] e End of the string.
+ * @param[in] enc Encoding.
+ * @retval NULL No previous character.
+ * @retval otherwise Pointer to the head of the previous character.
+ */
+static inline char *
+rb_enc_prev_char(const char *s, const char *p, const char *e, rb_encoding *enc)
+{
+ const OnigUChar *us = RBIMPL_CAST((const OnigUChar *)s);
+ const OnigUChar *up = RBIMPL_CAST((const OnigUChar *)p);
+ const OnigUChar *ue = RBIMPL_CAST((const OnigUChar *)e);
+ OnigUChar *ur = onigenc_get_prev_char_head(enc, us, up, ue);
+
+ return RBIMPL_CAST((char *)ur);
+}
+
+/**
+ * Queries the left boundary of a character. This function takes a pointer
+ * that is not necessarily a head of a character, and searches for its head.
+ *
+ * @param[in] s Start of the string.
+ * @param[in] p Pointer to a possibly-middle of a character.
+ * @param[in] e End of the string.
+ * @param[in] enc Encoding.
+ * @return Pointer to the head of the character that contains `p`.
+ */
+static inline char *
+rb_enc_left_char_head(const char *s, const char *p, const char *e, rb_encoding *enc)
+{
+ const OnigUChar *us = RBIMPL_CAST((const OnigUChar *)s);
+ const OnigUChar *up = RBIMPL_CAST((const OnigUChar *)p);
+ const OnigUChar *ue = RBIMPL_CAST((const OnigUChar *)e);
+ OnigUChar *ur = onigenc_get_left_adjust_char_head(enc, us, up, ue);
+
+ return RBIMPL_CAST((char *)ur);
+}
+
+/**
+ * Queries the right boundary of a character. This function takes a pointer
+ * that is not necessarily a head of a character, and searches for its tail.
+ *
+ * @param[in] s Start of the string.
+ * @param[in] p Pointer to a possibly-middle of a character.
+ * @param[in] e End of the string.
+ * @param[in] enc Encoding.
+ * @return Pointer to the end of the character that contains `p`.
+ */
+static inline char *
+rb_enc_right_char_head(const char *s, const char *p, const char *e, rb_encoding *enc)
+{
+ const OnigUChar *us = RBIMPL_CAST((const OnigUChar *)s);
+ const OnigUChar *up = RBIMPL_CAST((const OnigUChar *)p);
+ const OnigUChar *ue = RBIMPL_CAST((const OnigUChar *)e);
+ OnigUChar *ur = onigenc_get_right_adjust_char_head(enc, us, up, ue);
+
+ return RBIMPL_CAST((char *)ur);
+}
+
+/**
+ * Scans the string backwards for n characters.
+ *
+ * @param[in] s Start of the string.
+ * @param[in] p Pointer to a character.
+ * @param[in] e End of the string.
+ * @param[in] n Steps.
+ * @param[in] enc Encoding.
+ * @retval NULL There are no `n` characters left.
+ * @retval otherwise Pointer to `n` character before `p`.
+ */
+static inline char *
+rb_enc_step_back(const char *s, const char *p, const char *e, int n, rb_encoding *enc)
+{
+ const OnigUChar *us = RBIMPL_CAST((const OnigUChar *)s);
+ const OnigUChar *up = RBIMPL_CAST((const OnigUChar *)p);
+ const OnigUChar *ue = RBIMPL_CAST((const OnigUChar *)e);
+ const OnigUChar *ur = onigenc_step_back(enc, us, up, ue, n);
+
+ return RBIMPL_CAST((char *)ur);
+}
+
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_enc_asciicompat(). People don't use
+ * it directly. Just always use rb_enc_asciicompat().
+ *
+ * @param[in] enc Encoding in question.
+ * @retval 1 It is ASCII compatible.
+ * @retval 0 It isn't.
+ */
+static inline int
+rb_enc_asciicompat_inline(rb_encoding *enc)
+{
+ return rb_enc_mbminlen(enc)==1 && !rb_enc_dummy_p(enc);
+}
+
+/**
+ * Queries if the passed encoding is _in some sense_ compatible with ASCII.
+ * The concept of ASCII compatibility is nuanced, and private to our
+ * implementation. For instance SJIS is ASCII compatible to us, despite their
+ * having different characters at code point `0x5C`. This is based on some
+ * practical consideration that Japanese people confuses SJIS to be "upper
+ * compatible" with ASCII (which is in fact a wrong idea, but we just don't go
+ * strict here). An example of ASCII incompatible encoding is UTF-16. UTF-16
+ * shares code points with ASCII, but employs a completely different encoding
+ * scheme.
+ *
+ * @param[in] enc Encoding in question.
+ * @retval 0 It is incompatible.
+ * @retval 1 It is compatible.
+ */
+static inline bool
+rb_enc_asciicompat(rb_encoding *enc)
+{
+ if (rb_enc_mbminlen(enc) != 1) {
+ return false;
+ }
+ else if (rb_enc_dummy_p(enc)) {
+ return false;
+ }
+ else {
+ return true;
+ }
+}
+
+/**
+ * Queries if the passed string is in an ASCII-compatible encoding.
+ *
+ * @param[in] str A Ruby's string to query.
+ * @retval 0 `str` is not a String, or an ASCII-incompatible string.
+ * @retval 1 Otherwise.
+ */
+static inline bool
+rb_enc_str_asciicompat_p(VALUE str)
+{
+ rb_encoding *enc = rb_enc_get(str);
+
+ return rb_enc_asciicompat(enc);
+}
+
+/**
+ * Queries the Ruby-level counterpart instance of ::rb_cEncoding that
+ * corresponds to the passed encoding.
+ *
+ * @param[in] enc An encoding
+ * @retval RUBY_Qnil `enc` is a null pointer.
+ * @retval otherwise An instance of ::rb_cEncoding.
+ */
+VALUE rb_enc_from_encoding(rb_encoding *enc);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries if the passed encoding is either one of UTF-8/16/32.
+ *
+ * @note It does not take UTF-7, which we actually support, into account.
+ *
+ * @param[in] enc Encoding in question.
+ * @retval 0 It is not a Unicode variant.
+ * @retval otherwise It is.
+ *
+ * @internal
+ *
+ * In reality it returns 1/0, but the value is abstracted as
+ * `ONIGENC_FLAG_UNICODE`.
+ */
+int rb_enc_unicode_p(rb_encoding *enc);
+
+RBIMPL_ATTR_RETURNS_NONNULL()
+/**
+ * Queries the encoding that represents ASCII-8BIT a.k.a. binary.
+ *
+ * @return The encoding that represents ASCII-8BIT.
+ *
+ * @internal
+ *
+ * This can not return NULL once the process properly boots up.
+ */
+rb_encoding *rb_ascii8bit_encoding(void);
+
+RBIMPL_ATTR_RETURNS_NONNULL()
+/**
+ * Queries the encoding that represents UTF-8.
+ *
+ * @return The encoding that represents UTF-8.
+ *
+ * @internal
+ *
+ * This can not return NULL once the process properly boots up.
+ */
+rb_encoding *rb_utf8_encoding(void);
+
+RBIMPL_ATTR_RETURNS_NONNULL()
+/**
+ * Queries the encoding that represents US-ASCII.
+ *
+ * @return The encoding that represents US-ASCII.
+ *
+ * @internal
+ *
+ * This can not return NULL once the process properly boots up.
+ */
+rb_encoding *rb_usascii_encoding(void);
+
+/**
+ * Queries the encoding that represents the current locale.
+ *
+ * @return The encoding that represents the process' locale.
+ *
+ * @internal
+ *
+ * This is dynamic. If you change the process' locale by e.g. calling
+ * `setlocale(3)`, that should also change the return value of this function.
+ *
+ * There is no official way for Ruby scripts to manipulate locales, though.
+ */
+rb_encoding *rb_locale_encoding(void);
+
+/**
+ * Queries the "filesystem" encoding. This is the encoding that ruby expects
+ * info from the OS' file system are in. This affects for instance return
+ * value of rb_dir_getwd(). Most notably on Windows it can be an alias of OS
+ * codepage. Most notably on Linux users can set this via default external
+ * encoding.
+ *
+ * @return The "filesystem" encoding.
+ */
+rb_encoding *rb_filesystem_encoding(void);
+
+/**
+ * Queries the "default external" encoding. This is used to interact with
+ * outer-process things such as File. Though not recommended, you can set this
+ * using rb_enc_set_default_external().
+ *
+ * @return The "default external" encoding.
+ */
+rb_encoding *rb_default_external_encoding(void);
+
+/**
+ * Queries the "default internal" encoding. This could be a null pointer.
+ * Otherwise, outer-process info are transcoded from default external encoding
+ * to this one during reading from an IO.
+ *
+ * @return The "default internal" encoding (if any).
+ */
+rb_encoding *rb_default_internal_encoding(void);
+
+#ifndef rb_ascii8bit_encindex
+RBIMPL_ATTR_CONST()
+/**
+ * Identical to rb_ascii8bit_encoding(), except it returns the encoding's index
+ * instead of the encoding itself.
+ *
+ * @return The index of encoding of ASCII-8BIT.
+ *
+ * @internal
+ *
+ * This happens to be 0.
+ */
+int rb_ascii8bit_encindex(void);
+#endif
+
+/**
+ * Queries if the passed object is in ascii 8bit (== binary) encoding. The
+ * object must be capable of having inline encoding. Using this macro needs
+ * deep understanding of bit level object binary layout.
+ *
+ * @param[in] obj An object to check.
+ * @retval 1 It is.
+ * @retval 0 It isn't.
+ */
+static inline bool
+RB_ENCODING_IS_ASCII8BIT(VALUE obj)
+{
+ return RB_ENCODING_GET_INLINED(obj) == rb_ascii8bit_encindex();
+}
+
+#ifndef rb_utf8_encindex
+RBIMPL_ATTR_CONST()
+/**
+ * Identical to rb_utf8_encoding(), except it returns the encoding's index
+ * instead of the encoding itself.
+ *
+ * @return The index of encoding of UTF-8.
+ */
+int rb_utf8_encindex(void);
+#endif
+
+#ifndef rb_usascii_encindex
+RBIMPL_ATTR_CONST()
+/**
+ * Identical to rb_usascii_encoding(), except it returns the encoding's index
+ * instead of the encoding itself.
+ *
+ * @return The index of encoding of UTF-8.
+ */
+int rb_usascii_encindex(void);
+#endif
+
+/**
+ * Identical to rb_locale_encoding(), except it returns the encoding's index
+ * instead of the encoding itself.
+ *
+ * @return The index of the locale encoding.
+ */
+int rb_locale_encindex(void);
+
+/**
+ * Identical to rb_filesystem_encoding(), except it returns the encoding's
+ * index instead of the encoding itself.
+ *
+ * @return The index of the filesystem encoding.
+ */
+int rb_filesystem_encindex(void);
+
+/**
+ * Identical to rb_default_external_encoding(), except it returns the
+ * Ruby-level counterpart instance of ::rb_cEncoding that corresponds to the
+ * default external encoding.
+ *
+ * @return An instance of ::rb_cEncoding of default external.
+ */
+VALUE rb_enc_default_external(void);
+
+/**
+ * Identical to rb_default_internal_encoding(), except it returns the
+ * Ruby-level counterpart instance of ::rb_cEncoding that corresponds to the
+ * default internal encoding.
+ *
+ * @return An instance of ::rb_cEncoding of default internal.
+ */
+VALUE rb_enc_default_internal(void);
+
+/**
+ * Destructively assigns the passed encoding as the default external encoding.
+ * You should not use this API. It has process-global side effects. Also it
+ * doesn't change encodings of strings that have already been read.
+ *
+ * @param[in] encoding Ruby level encoding.
+ * @exception rb_eArgError `encoding` is ::RUBY_Qnil.
+ * @post The default external encoding is `encoding`.
+ */
+void rb_enc_set_default_external(VALUE encoding);
+
+/**
+ * Destructively assigns the passed encoding as the default internal encoding.
+ * You should not use this API. It has process-global side effects. Also it
+ * doesn't change encodings of strings that have already been read.
+ *
+ * @param[in] encoding Ruby level encoding.
+ * @post The default internal encoding is `encoding`.
+ * @note Unlike rb_enc_set_default_external() you can pass ::RUBY_Qnil.
+ */
+void rb_enc_set_default_internal(VALUE encoding);
+
+/**
+ * Returns a platform-depended "charmap" of the current locale. This
+ * information is called a "Codeset name" in IEEE 1003.1 section 13
+ * (`<langinfo.h>`). This is a very low-level API. The return value can have
+ * no corresponding encoding when passed to rb_find_encoding().
+ *
+ * @param[in] klass Ignored for no reason (why...)
+ * @return The low-level locale charmap, in Ruby's String.
+ */
+VALUE rb_locale_charmap(VALUE klass);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/** @cond INTERNAL_MACRO */
+#define RB_ENCODING_GET RB_ENCODING_GET
+#define RB_ENCODING_GET_INLINED RB_ENCODING_GET_INLINED
+#define RB_ENCODING_IS_ASCII8BIT RB_ENCODING_IS_ASCII8BIT
+#define RB_ENCODING_SET RB_ENCODING_SET
+#define RB_ENCODING_SET_INLINED RB_ENCODING_SET_INLINED
+#define rb_enc_asciicompat rb_enc_asciicompat
+#define rb_enc_code_to_mbclen rb_enc_code_to_mbclen
+#define rb_enc_codepoint rb_enc_codepoint
+#define rb_enc_left_char_head rb_enc_left_char_head
+#define rb_enc_mbc_to_codepoint rb_enc_mbc_to_codepoint
+#define rb_enc_mbcput rb_enc_mbcput
+#define rb_enc_mbmaxlen rb_enc_mbmaxlen
+#define rb_enc_mbminlen rb_enc_mbminlen
+#define rb_enc_name rb_enc_name
+#define rb_enc_prev_char rb_enc_prev_char
+#define rb_enc_right_char_head rb_enc_right_char_head
+#define rb_enc_step_back rb_enc_step_back
+#define rb_enc_str_asciicompat_p rb_enc_str_asciicompat_p
+/** @endcond */
+
+#endif /* RUBY_INTERNAL_ENCODING_ENCODING_H */
diff --git a/include/ruby/internal/encoding/pathname.h b/include/ruby/internal/encoding/pathname.h
new file mode 100644
index 0000000000..0b5e85a524
--- /dev/null
+++ b/include/ruby/internal/encoding/pathname.h
@@ -0,0 +1,184 @@
+#ifndef RUBY_INTERNAL_ENCODING_PATHNAME_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_PATHNAME_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate encodings of pathnames.
+ */
+
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/encoding/encoding.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Returns a path component directly adjacent to the passed pointer.
+ *
+ * ```
+ * "/multi/byte/encoded/pathname.txt"
+ * ^ ^ ^
+ * | | +--- end
+ * | +--- @return
+ * +--- path
+ * ```
+ *
+ * @param[in] path Where to start scanning.
+ * @param[in] end End of the path string.
+ * @param[in] enc Encoding of the string.
+ * @return A pointer in the passed string where the next path component
+ * resides, or `end` if there is no next path component.
+ */
+char *rb_enc_path_next(const char *path, const char *end, rb_encoding *enc);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Seeks for non-prefix part of a pathname. This can be a no-op when the OS
+ * has no such concept like a path prefix. But there are OSes where path
+ * prefixes do exist.
+ *
+ * ```
+ * "C:\multi\byte\encoded\pathname.txt"
+ * ^ ^ ^
+ * | | +--- end
+ * | +--- @return
+ * +--- path
+ * ```
+ *
+ * @param[in] path Where to start scanning.
+ * @param[in] end End of the path string.
+ * @param[in] enc Encoding of the string.
+ * @return A pointer in the passed string where non-prefix part starts, or
+ * `path` if the OS does not have path prefix.
+ */
+char *rb_enc_path_skip_prefix(const char *path, const char *end, rb_encoding *enc);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Returns the last path component.
+ *
+ * ```
+ * "/multi/byte/encoded/pathname.txt"
+ * ^ ^ ^
+ * | | +--- end
+ * | +--- @return
+ * +--- path
+ * ```
+ *
+ * @param[in] path Where to start scanning.
+ * @param[in] end End of the path string.
+ * @param[in] enc Encoding of the string.
+ * @return A pointer in the passed string where the last path component
+ * resides, or `end` if there is no more path component.
+ */
+char *rb_enc_path_last_separator(const char *path, const char *end, rb_encoding *enc);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This just returns the passed end basically. It makes difference in case the
+ * passed string ends with tons of path separators like the following:
+ *
+ * ```
+ * "/path/that/ends/with/lots/of/slashes//////////////"
+ * ^ ^ ^
+ * | | +--- end
+ * | +--- @return
+ * +--- path
+ * ```
+ *
+ * @param[in] path Where to start scanning.
+ * @param[in] end End of the path string.
+ * @param[in] enc Encoding of the string.
+ * @return A pointer in the passed string where the trailing path
+ * separators start, or `end` if there is no trailing path
+ * separators.
+ *
+ * @internal
+ *
+ * It seems this function was introduced to mimic what POSIX says about
+ * `basename(3)`.
+ */
+char *rb_enc_path_end(const char *path, const char *end, rb_encoding *enc);
+
+RBIMPL_ATTR_NONNULL((1, 4))
+/**
+ * Our own encoding-aware version of `basename(3)`. Normally, this function
+ * returns the last path component of the given name. However in case the
+ * passed name ends with a path separator, it returns the name of the
+ * directory, not the last (empty) component. Also if the passed name is a
+ * root directory, it returns that root directory. Note however that Windows
+ * filesystem have drive letters, which this function does not return.
+ *
+ * @param[in] name Target path.
+ * @param[out] baselen Return buffer.
+ * @param[in,out] alllen Number of bytes of `name`.
+ * @param[enc] enc Encoding of `name`.
+ * @return The rightmost component of `name`.
+ * @post `baselen`, if passed, is updated to be the number of bytes
+ * of the returned basename.
+ * @post `alllen`, if passed, is updated to be the number of bytes of
+ * strings not considered as the basename.
+ */
+const char *ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc);
+
+RBIMPL_ATTR_NONNULL((1, 3))
+/**
+ * Our own encoding-aware version of `extname`. This function first applies
+ * rb_enc_path_last_separator() to the passed name and only concerns its return
+ * value (ignores any parent directories). This function returns complicated
+ * results:
+ *
+ * ```CXX
+ * auto path = "...";
+ * auto len = strlen(path);
+ * auto ret = ruby_enc_find_extname(path, &len, rb_ascii8bit_encoding());
+ *
+ * switch(len) {
+ * case 0:
+ * if (ret == 0) {
+ * // `path` is a file without extensions.
+ * }
+ * else {
+ * // `path` is a dotfile.
+ * // `ret` is the file's name.
+ * }
+ * break;
+ *
+ * case 1:
+ * // `path` _ends_ with a dot.
+ * // `ret` is that dot.
+ * break;
+ *
+ * default:
+ * // `path` has an extension.
+ * // `ret` is that extension.
+ * }
+ * ```
+ *
+ * @param[in] name Target path.
+ * @param[in,out] len Number of bytes of `name`.
+ * @param[in] enc Encoding of `name`.
+ * @return See above.
+ * @post `len`, if passed, is updated (see above).
+ */
+const char *ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_INTERNAL_ENCODING_PATHNAME_H */
diff --git a/include/ruby/internal/encoding/re.h b/include/ruby/internal/encoding/re.h
new file mode 100644
index 0000000000..d0de23bc83
--- /dev/null
+++ b/include/ruby/internal/encoding/re.h
@@ -0,0 +1,46 @@
+#ifndef RUBY_INTERNAL_ENCODING_RE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_RE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate encodings of symbols.
+ */
+
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/encoding/encoding.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Identical to rb_reg_new(), except it additionally takes an encoding.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @param[in] enc Encoding of `ptr`.
+ * @param[in] opts Options e.g. ONIG_OPTION_MULTILINE.
+ * @exception rb_eRegexpError Failed to compile `ptr`.
+ * @return An allocated new instance of ::rb_cRegexp, of `enc` encoding,
+ * whose expression is compiled according to `ptr`.
+ */
+VALUE rb_enc_reg_new(const char *ptr, long len, rb_encoding *enc, int opts);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_INTERNAL_ENCODING_RE_H */
diff --git a/include/ruby/internal/encoding/sprintf.h b/include/ruby/internal/encoding/sprintf.h
new file mode 100644
index 0000000000..cb8737b414
--- /dev/null
+++ b/include/ruby/internal/encoding/sprintf.h
@@ -0,0 +1,78 @@
+#ifndef RUBY_INTERNAL_ENCODING_SPRINTF_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_SPRINTF_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate encodings of symbols.
+ */
+#include "ruby/internal/config.h"
+#include <stdarg.h>
+#include "ruby/internal/attr/format.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/encoding/encoding.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Identical to rb_sprintf(), except it additionally takes an encoding. The
+ * passed encoding rules both the incoming format specifier and the resulting
+ * string.
+ *
+ * @param[in] enc Encoding of `fmt`.
+ * @param[in] fmt A `printf`-like format specifier.
+ * @param[in] ... Variadic number of contents to format.
+ * @return A rendered new instance of ::rb_cString, of `enc` encoding.
+ */
+VALUE rb_enc_sprintf(rb_encoding *enc, const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 0)
+/**
+ * Identical to rb_enc_sprintf(), except it takes a `va_list` instead of
+ * variadic arguments. It can also be seen as a routine identical to
+ * rb_vsprintf(), except it additionally takes an encoding.
+ *
+ * @param[in] enc Encoding of `fmt`.
+ * @param[in] fmt A `printf`-like format specifier.
+ * @param[in] ap Contents to format.
+ * @return A rendered new instance of ::rb_cString, of `enc` encoding.
+ */
+VALUE rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((3))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
+/**
+ * Identical to rb_raise(), except it additionally takes an encoding.
+ *
+ * @param[in] enc Encoding of the generating exception.
+ * @param[in] exc A subclass of ::rb_eException.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @param[in] ... Contents of the message.
+ * @exception exc The specified exception.
+ * @note It never returns.
+ */
+void rb_enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_INTERNAL_ENCODING_SPRINTF_H */
diff --git a/include/ruby/internal/encoding/string.h b/include/ruby/internal/encoding/string.h
new file mode 100644
index 0000000000..2cfa91c01e
--- /dev/null
+++ b/include/ruby/internal/encoding/string.h
@@ -0,0 +1,346 @@
+#ifndef RUBY_INTERNAL_ENCODING_STRING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_STRING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate encodings of strings.
+ */
+
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/encoding/encoding.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/intern/string.h" /* rbimpl_strlen */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Identical to rb_str_new(), except it additionally takes an encoding.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @param[in] enc Encoding of `ptr`.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString, of `len` bytes length, of `enc`
+ * encoding, whose contents are verbatim copy of `ptr`.
+ * @pre At least `len` bytes of continuous memory region shall be
+ * accessible via `ptr`.
+ * @note `enc` can be a null pointer. It can also be seen as a routine
+ * identical to rb_usascii_str_new() then.
+ */
+VALUE rb_enc_str_new(const char *ptr, long len, rb_encoding *enc);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_enc_str_new(), except it assumes the passed pointer is a
+ * pointer to a C string. It can also be seen as a routine identical to
+ * rb_str_new_cstr(), except it additionally takes an encoding.
+ *
+ * @param[in] ptr A C string.
+ * @param[in] enc Encoding of `ptr`.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString, of `enc` encoding, whose contents
+ * are verbatim copy of `ptr`.
+ * @pre `ptr` must not be a null pointer.
+ * @pre Because `ptr` is a C string it makes no sense for `enc` to be
+ * something like UTF-32.
+ * @note `enc` can be a null pointer. It can also be seen as a routine
+ * identical to rb_usascii_str_new_cstr() then.
+ */
+VALUE rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc);
+
+/**
+ * Identical to rb_enc_str_new(), except it takes a C string literal. It can
+ * also be seen as a routine identical to rb_str_new_static(), except it
+ * additionally takes an encoding.
+ *
+ * @param[in] ptr A C string literal.
+ * @param[in] len `strlen(ptr)`.
+ * @param[in] enc Encoding of `ptr`.
+ * @exception rb_eArgError `len` out of range of `size_t`.
+ * @pre `ptr` must be a C string constant.
+ * @return An instance of ::rb_cString, of `enc` encoding, whose backend
+ * storage is the passed C string literal.
+ * @warning It is a very bad idea to write to a C string literal (often
+ * immediate SEGV shall occur). Consider return values of this
+ * function be read-only.
+ * @note `enc` can be a null pointer. It can also be seen as a routine
+ * identical to rb_usascii_str_new_static() then.
+ */
+VALUE rb_enc_str_new_static(const char *ptr, long len, rb_encoding *enc);
+
+/**
+ * Identical to rb_enc_str_new(), except it returns a "f"string. It can also
+ * be seen as a routine identical to rb_interned_str(), except it additionally
+ * takes an encoding.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @param[in] enc Encoding of `ptr`.
+ * @exception rb_eArgError `len` is negative.
+ * @return A found or created instance of ::rb_cString, of `len` bytes
+ * length, of `enc` encoding, whose contents are identical to that
+ * of `ptr`.
+ * @pre At least `len` bytes of continuous memory region shall be
+ * accessible via `ptr`.
+ * @note `enc` can be a null pointer.
+ */
+VALUE rb_enc_interned_str(const char *ptr, long len, rb_encoding *enc);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_enc_str_new_cstr(), except it returns a "f"string. It can
+ * also be seen as a routine identical to rb_interned_str_cstr(), except it
+ * additionally takes an encoding.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] enc Encoding of `ptr`.
+ * @return A found or created instance of ::rb_cString of `enc` encoding,
+ * whose contents are identical to that of `ptr`.
+ * @pre At least `len` bytes of continuous memory region shall be
+ * accessible via `ptr`.
+ * @note `enc` can be a null pointer.
+ */
+VALUE rb_enc_interned_str_cstr(const char *ptr, rb_encoding *enc);
+
+/**
+ * Counts the number of characters of the passed string, according to the
+ * passed encoding. This has to be complicated. The passed string could be
+ * invalid and/or broken. This routine would scan from the beginning til the
+ * end, byte by byte, to seek out character boundaries. Could be super slow.
+ *
+ * @param[in] head Leftmost pointer to the string.
+ * @param[in] tail Rightmost pointer to the string.
+ * @param[in] enc Encoding of the string.
+ * @return Number of characters exist in `head` .. `tail`. The definition
+ * of "character" depends on the passed `enc`.
+ */
+long rb_enc_strlen(const char *head, const char *tail, rb_encoding *enc);
+
+/**
+ * Queries the n-th character. Like rb_enc_strlen() this function can be fast
+ * or slow depending on the contents. Don't expect characters to be uniformly
+ * distributed across the entire string.
+ *
+ * @param[in] head Leftmost pointer to the string.
+ * @param[in] tail Rightmost pointer to the string.
+ * @param[in] nth Requested index of characters.
+ * @param[in] enc Encoding of the string.
+ * @return Pointer to the first byte of the character that is `nth`
+ * character ahead of `head`, or `tail` if there is no such
+ * character (OOB etc). The definition of "character" depends on
+ * the passed `enc`.
+ */
+char *rb_enc_nth(const char *head, const char *tail, long nth, rb_encoding *enc);
+
+/**
+ * Identical to rb_enc_get_index(), except the return type.
+ *
+ * @param[in] obj Object in question.
+ * @exception rb_eTypeError `obj` is incapable of having an encoding.
+ * @return `obj`'s encoding.
+ */
+VALUE rb_obj_encoding(VALUE obj);
+
+/**
+ * Identical to rb_str_cat(), except it additionally takes an encoding.
+ *
+ * @param[out] str Destination object.
+ * @param[in] ptr Contents to append.
+ * @param[in] len Length of `src`, in bytes.
+ * @param[in] enc Encoding of `ptr`.
+ * @exception rb_eArgError `len` is negative.
+ * @exception rb_eEncCompatError `enc` is not compatible with `str`.
+ * @return The passed `dst`.
+ * @post The contents of `ptr` is copied, transcoded into `dst`'s
+ * encoding, then pasted into `dst`'s end.
+ */
+VALUE rb_enc_str_buf_cat(VALUE str, const char *ptr, long len, rb_encoding *enc);
+
+/**
+ * Encodes the passed code point into a series of bytes.
+ *
+ * @param[in] code Code point.
+ * @param[in] enc Target encoding scheme.
+ * @exception rb_eRangeError `enc` does not glean `code`.
+ * @return An instance of ::rb_cString, of `enc` encoding, whose sole
+ * contents is `code` represented in `enc`.
+ * @note No way to encode code points bigger than UINT_MAX.
+ *
+ * @internal
+ *
+ * In other languages, APIs like this one could be seen as the primitive
+ * routines where encodings' "encode" feature are implemented. However in case
+ * of Ruby this is not the primitive one. We directly manipulate encoded
+ * strings. Encoding conversion routines transcode an encoded string directly
+ * to another one; not via a code point array.
+ */
+VALUE rb_enc_uint_chr(unsigned int code, rb_encoding *enc);
+
+/**
+ * Identical to rb_external_str_new(), except it additionally takes an
+ * encoding. However the whole point of rb_external_str_new() is to encode a
+ * string into default external encoding. Being able to specify arbitrary
+ * encoding just ruins the designed purpose the function meseems.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @param[in] enc Target encoding scheme.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to `enc` is fully defined over the given
+ * contents, then the return value is a string of `enc` encoding,
+ * whose contents are the converted ones. Otherwise the string is
+ * a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ *
+ * @internal
+ *
+ * @shyouhei has no idea why this one does not follow the naming convention
+ * that others obey. It seems to him that this should have been called
+ * `rb_enc_external_str_new`.
+ */
+VALUE rb_external_str_new_with_enc(const char *ptr, long len, rb_encoding *enc);
+
+/**
+ * Identical to rb_str_export(), except it additionally takes an encoding.
+ *
+ * @param[in] obj Target object.
+ * @param[in] enc Target encoding.
+ * @exception rb_eTypeError No implicit conversion to String.
+ * @return Converted ruby string of `enc` encoding.
+ */
+VALUE rb_str_export_to_enc(VALUE obj, rb_encoding *enc);
+
+/**
+ * Encoding conversion main routine.
+ *
+ * @param[in] str String to convert.
+ * @param[in] from Source encoding.
+ * @param[in] to Destination encoding.
+ * @return A copy of `str`, with conversion from `from` to `to` applied.
+ * @note `from` can be a null pointer. `str`'s encoding is taken then.
+ * @note `to` can be a null pointer. No-op then.
+ */
+VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to);
+
+/**
+ * Identical to rb_str_conv_enc(), except it additionally takes IO encoder
+ * options. The extra arguments can be constructed using io_extract_modeenc()
+ * etc.
+ *
+ * @param[in] str String to convert.
+ * @param[in] from Source encoding.
+ * @param[in] to Destination encoding.
+ * @param[in] ecflags A set of enum ::ruby_econv_flag_type.
+ * @param[in] ecopts Optional hash.
+ * @return A copy of `str`, with conversion from `from` to `to` applied.
+ * @note `from` can be a null pointer. `str`'s encoding is taken then.
+ * @note `to` can be a null pointer. No-op then.
+ * @note `ecopts` can be ::RUBY_Qnil, which is equivalent to passing an
+ * empty hash.
+ */
+VALUE rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags, VALUE ecopts);
+
+/**
+ * Scans the passed string to collect its code range. Because a Ruby's string
+ * is mutable, its contents change from time to time; so does its code range.
+ * A long-lived string tends to fall back to ::RUBY_ENC_CODERANGE_UNKNOWN.
+ * This API scans it and re-assigns a fine-grained code range constant.
+ *
+ * @param[out] str A string.
+ * @return An enum ::ruby_coderange_type.
+ */
+int rb_enc_str_coderange(VALUE str);
+
+/**
+ * Scans the passed string until it finds something odd. Returns the number of
+ * bytes scanned. As the name implies this is suitable for repeated call. One
+ * of its application is `IO#readlines`. The method reads from its receiver's
+ * read buffer, maybe more than once, looking for newlines. But "newline" can
+ * be different among encodings. This API is used to detect broken contents to
+ * properly mark them as such.
+ *
+ * @param[in] str String to scan.
+ * @param[in] end End of `str`.
+ * @param[in] enc `str`'s encoding.
+ * @param[out] cr Return buffer.
+ * @return Distance between `str` and first such byte where broken.
+ * @post `cr` has the code range type.
+ */
+long rb_str_coderange_scan_restartable(const char *str, const char *end, rb_encoding *enc, int *cr);
+
+/**
+ * Queries if the passed string is "ASCII only". An ASCII only string is a
+ * string who doesn't have any non-ASCII characters at all. This doesn't
+ * necessarily mean the string is in ASCII encoding. For instance a String of
+ * CP932 encoding can quite much be ASCII only, depending on its contents.
+ *
+ * @param[in] str String in question.
+ * @retval 1 It doesn't have non-ASCII characters.
+ * @retval 0 It has characters that are out of ASCII.
+ */
+int rb_enc_str_asciionly_p(VALUE str);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Looks for the passed string in the passed buffer.
+ *
+ * @param[in] x Query string.
+ * @param[in] m Number of bytes of `x`.
+ * @param[in] y Buffer that potentially includes `x`.
+ * @param[in] n Number of bytes of `y`.
+ * @param[in] enc Encoding of both `x` and `y`.
+ * @retval -1 Not found.
+ * @retval otherwise Found index in `y`.
+ * @note This API can match at a non-character-boundary.
+ */
+long rb_memsearch(const void *x, long m, const void *y, long n, rb_encoding *enc);
+
+/** @cond INTERNAL_MACRO */
+RBIMPL_ATTR_NONNULL(())
+static inline VALUE
+rbimpl_enc_str_new_cstr(const char *str, rb_encoding *enc)
+{
+ long len = rbimpl_strlen(str);
+
+ return rb_enc_str_new_static(str, len, enc);
+}
+
+#define rb_enc_str_new(str, len, enc) \
+ ((RBIMPL_CONSTANT_P(str) && \
+ RBIMPL_CONSTANT_P(len) ? \
+ rb_enc_str_new_static: \
+ rb_enc_str_new) ((str), (len), (enc)))
+
+#define rb_enc_str_new_cstr(str, enc) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_enc_str_new_cstr : \
+ rb_enc_str_new_cstr) ((str), (enc)))
+
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_INTERNAL_ENCODING_STRING_H */
diff --git a/include/ruby/internal/encoding/symbol.h b/include/ruby/internal/encoding/symbol.h
new file mode 100644
index 0000000000..9cd1b0dbf4
--- /dev/null
+++ b/include/ruby/internal/encoding/symbol.h
@@ -0,0 +1,100 @@
+#ifndef RUBY_INTERNAL_ENCODING_SYMBOL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_SYMBOL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Routines to manipulate encodings of symbols.
+ */
+
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/encoding/encoding.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Identical to rb_intern2(), except it additionally takes an encoding.
+ *
+ * @param[in] name The name of the id.
+ * @param[in] len Length of `name`.
+ * @param[in] enc `name`'s encoding.
+ * @exception rb_eRuntimeError Too many symbols.
+ * @return A (possibly new) id whose value is the given name.
+ * @note These days Ruby internally has two kinds of symbols
+ * (static/dynamic). Symbols created using this function would
+ * become static ones; i.e. would never be garbage collected. It
+ * is up to you to avoid memory leaks. Think twice before using
+ * it.
+ */
+ID rb_intern3(const char *name, long len, rb_encoding *enc);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_symname_p(), except it additionally takes an encoding.
+ *
+ * @param[in] str A C string to check.
+ * @param[in] enc `str`'s encoding.
+ * @retval 1 It is a valid symbol name.
+ * @retval 0 It is invalid as a symbol name.
+ */
+int rb_enc_symname_p(const char *str, rb_encoding *enc);
+
+/**
+ * Identical to rb_enc_symname_p(), except it additionally takes the passed
+ * string's length. This is needed for strings containing NUL bytes, like in
+ * case of UTF-32.
+ *
+ * @param[in] name A C string to check.
+ * @param[in] len Number of bytes of `str`.
+ * @param[in] enc `str`'s encoding.
+ * @retval 1 It is a valid symbol name.
+ * @retval 0 It is invalid as a symbol name.
+ */
+int rb_enc_symname2_p(const char *name, long len, rb_encoding *enc);
+
+/**
+ * Identical to rb_check_id(), except it takes a pointer to a memory region
+ * instead of Ruby's string.
+ *
+ * @param[in] ptr A pointer to a memory region.
+ * @param[in] len Number of bytes of `ptr`.
+ * @param[in] enc Encoding of `ptr`.
+ * @exception rb_eEncodingError `ptr` contains non-ASCII according to `enc`.
+ * @retval 0 No such id ever existed in the history.
+ * @retval otherwise The id that represents the given name.
+ */
+ID rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc);
+
+/**
+ * Identical to rb_check_id_cstr(), except for the return type. It can also be
+ * seen as a routine identical to rb_check_symbol(), except it takes a pointer
+ * to a memory region instead of Ruby's string.
+ *
+ * @param[in] ptr A pointer to a memory region.
+ * @param[in] len Number of bytes of `ptr`.
+ * @param[in] enc Encoding of `ptr`.
+ * @exception rb_eEncodingError `ptr` contains non-ASCII according to `enc`.
+ * @retval RUBY_Qnil No such id ever existed in the history.
+ * @retval otherwise The id that represents the given name.
+ */
+VALUE rb_check_symbol_cstr(const char *ptr, long len, rb_encoding *enc);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_INTERNAL_ENCODING_SYMBOL_H */
diff --git a/include/ruby/internal/encoding/transcode.h b/include/ruby/internal/encoding/transcode.h
new file mode 100644
index 0000000000..7f26d2eae9
--- /dev/null
+++ b/include/ruby/internal/encoding/transcode.h
@@ -0,0 +1,562 @@
+#ifndef RUBY_INTERNAL_ENCODING_TRANSCODE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_INTERNAL_ENCODING_TRANSCODE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief econv stuff
+ */
+
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/** return value of rb_econv_convert() */
+typedef enum {
+
+ /**
+ * The conversion stopped when it found an invalid sequence.
+ */
+ econv_invalid_byte_sequence,
+
+ /**
+ * The conversion stopped when it found a character in the input which
+ * cannot be representable in the output.
+ */
+ econv_undefined_conversion,
+
+ /**
+ * The conversion stopped because there is no destination.
+ */
+ econv_destination_buffer_full,
+
+ /**
+ * The conversion stopped because there is no input.
+ */
+ econv_source_buffer_empty,
+
+ /**
+ * The conversion stopped after converting everything. This is arguably
+ * the expected normal end of conversion.
+ */
+ econv_finished,
+
+ /**
+ * The conversion stopped after writing something to somewhere, before
+ * reading everything.
+ */
+ econv_after_output,
+
+ /**
+ * The conversion stopped in middle of reading a character, possibly due to
+ * a partial read of a socket etc.
+ */
+ econv_incomplete_input
+} rb_econv_result_t;
+
+/** An opaque struct that represents a lowest level of encoding conversion. */
+typedef struct rb_econv_t rb_econv_t;
+
+/**
+ * Converts the contents of the passed string from its encoding to the passed
+ * one.
+ *
+ * @param[in] str Target string.
+ * @param[in] to Destination encoding.
+ * @param[in] ecflags A set of enum
+ * ::ruby_econv_flag_type.
+ * @param[in] ecopts A keyword hash, like
+ * ::rb_io_t::rb_io_enc_t::ecopts.
+ * @exception rb_eArgError Not fully converted.
+ * @exception rb_eInvalidByteSequenceError `str` is malformed.
+ * @exception rb_eUndefinedConversionError `str` has a character not
+ * representable using `to`.
+ * @exception rb_eConversionNotFoundError There is no known conversion from
+ * `str`'s encoding to `to`.
+ * @return A string whose encoding is `to`, and whose contents is converted
+ * contents of `str`.
+ * @note Use rb_econv_prepare_options() to generate `ecopts`.
+ */
+VALUE rb_str_encode(VALUE str, VALUE to, int ecflags, VALUE ecopts);
+
+/**
+ * Queries if there is more than one way to convert between the passed two
+ * encodings. Encoding conversion are has_and_belongs_to_many relationships.
+ * There could be no direct conversion defined for the passed pair. Ruby tries
+ * to find an indirect way to do so then. For instance ISO-8859-1 has no
+ * direct conversion to ISO-2022-JP. But there is ISO-8859-1 to UTF-8
+ * conversion; then there is UTF-8 to EUC-JP conversion; finally there also is
+ * EUC-JP to ISO-2022-JP conversion. So in short ISO-8859-1 can be converted
+ * to ISO-2022-JP using that path. This function returns true. Obviously not
+ * everything that can be represented using UTF-8 can also be represented using
+ * EUC-JP. Conversions in practice can fail depending on the actual input, and
+ * that renders exceptions in case of rb_str_encode().
+ *
+ * @param[in] from_encoding One encoding.
+ * @param[in] to_encoding Another encoding.
+ * @retval 0 No way to convert the two.
+ * @retval 1 At least one way to convert the two.
+ *
+ * @internal
+ *
+ * Practically @shyouhei knows no way for this function to return 0. It seems
+ * everything can eventually be converted to/from UTF-8, which connects
+ * everything.
+ */
+int rb_econv_has_convpath_p(const char* from_encoding, const char* to_encoding);
+
+/**
+ * Identical to rb_econv_prepare_opts(), except it additionally takes the
+ * initial value of flags. The extra bits are bitwise-ORed to the return
+ * value.
+ *
+ * @param[in] opthash Keyword arguments.
+ * @param[out] ecopts Return buffer.
+ * @param[in] ecflags Default set of enum ::ruby_econv_flag_type.
+ * @exception rb_eArgError Unknown/Broken values passed.
+ * @return Calculated set of enum ::ruby_econv_flag_type.
+ * @post `ecopts` holds a hash object suitable for
+ * ::rb_io_t::rb_io_enc_t::ecopts.
+ */
+int rb_econv_prepare_options(VALUE opthash, VALUE *ecopts, int ecflags);
+
+/**
+ * Splits a keyword arguments hash (that for instance `String#encode` took)
+ * into a set of enum ::ruby_econv_flag_type and a hash storing replacement
+ * characters etc.
+ *
+ * @param[in] opthash Keyword arguments.
+ * @param[out] ecopts Return buffer.
+ * @exception rb_eArgError Unknown/Broken values passed.
+ * @return Calculated set of enum ::ruby_econv_flag_type.
+ * @post `ecopts` holds a hash object suitable for
+ * ::rb_io_t::rb_io_enc_t::ecopts.
+ */
+int rb_econv_prepare_opts(VALUE opthash, VALUE *ecopts);
+
+/**
+ * Creates a new instance of struct ::rb_econv_t.
+ *
+ * @param[in] source_encoding Name of an encoding.
+ * @param[in] destination_encoding Name of another encoding.
+ * @param[in] ecflags A set of enum ::ruby_econv_flag_type.
+ * @exception rb_eArgError No such encoding.
+ * @retval NULL Failed to create a struct ::rb_econv_t.
+ * @retval otherwise Allocated struct ::rb_econv_t.
+ * @warning Return value must be passed to rb_econv_close() exactly once.
+ */
+rb_econv_t *rb_econv_open(const char *source_encoding, const char *destination_encoding, int ecflags);
+
+/**
+ * Identical to rb_econv_open(), except it additionally takes a hash of
+ * optional strings.
+ *
+ *
+ * @param[in] source_encoding Name of an encoding.
+ * @param[in] destination_encoding Name of another encoding.
+ * @param[in] ecflags A set of enum ::ruby_econv_flag_type.
+ * @param[in] ecopts Optional set of strings.
+ * @exception rb_eArgError No such encoding.
+ * @retval NULL Failed to create a struct ::rb_econv_t.
+ * @retval otherwise Allocated struct ::rb_econv_t.
+ * @warning Return value must be passed to rb_econv_close() exactly once.
+ */
+rb_econv_t *rb_econv_open_opts(const char *source_encoding, const char *destination_encoding, int ecflags, VALUE ecopts);
+
+/**
+ * Converts a string from an encoding to another.
+ *
+ * Possible flags are either ::RUBY_ECONV_PARTIAL_INPUT (means the source
+ * buffer is a part of much larger one), ::RUBY_ECONV_AFTER_OUTPUT (instructs
+ * the converter to stop after output before input), or both of them.
+ *
+ * @param[in,out] ec Conversion specification/state etc.
+ * @param[in] source_buffer_ptr Target string.
+ * @param[in] source_buffer_end End of target string.
+ * @param[out] destination_buffer_ptr Return buffer.
+ * @param[out] destination_buffer_end End of return buffer.
+ * @param[in] flags Flags (see above).
+ * @return The status of the conversion.
+ * @post `destination_buffer_ptr` holds conversion results.
+ */
+rb_econv_result_t rb_econv_convert(rb_econv_t *ec,
+ const unsigned char **source_buffer_ptr, const unsigned char *source_buffer_end,
+ unsigned char **destination_buffer_ptr, unsigned char *destination_buffer_end,
+ int flags);
+
+/**
+ * Destructs a converter. Note that a converter can have a buffer, and can be
+ * non-empty. Calling this would lose your data then.
+ *
+ * @param[out] ec The converter to destroy.
+ * @post `ec` is no longer a valid pointer.
+ */
+void rb_econv_close(rb_econv_t *ec);
+
+/**
+ * Assigns the replacement string. The string passed here would appear in
+ * converted string when it cannot represent its source counterpart. This can
+ * happen for instance you convert an emoji to ISO-8859-1.
+ *
+ * @param[out] ec Target converter.
+ * @param[in] str Replacement string.
+ * @param[in] len Number of bytes of `str`.
+ * @param[in] encname Name of encoding of `str`.
+ * @retval 0 Success.
+ * @retval -1 Failure (ENOMEM etc.).
+ * @post `ec`'s replacement string is set to `str`.
+ */
+int rb_econv_set_replacement(rb_econv_t *ec, const unsigned char *str, size_t len, const char *encname);
+
+/**
+ * "Decorate"s a converter. There are special kind of converters that
+ * transforms the contents, like replacing CR into CRLF. You can add such
+ * decorators to a converter using this API. By using this function a
+ * decorator is prepended at the beginning of a conversion sequence: in case of
+ * CRLF conversion, newlines are converted before encodings are converted.
+ *
+ * @param[out] ec Target converter to decorate.
+ * @param[in] decorator_name Name of decorator to prepend.
+ * @retval 0 Success.
+ * @retval -1 Failure (no such decorator etc.).
+ * @post Decorator works before encoding conversion happens.
+ *
+ * @internal
+ *
+ * What is the possible value of the `decorator_name` is not public. You have
+ * to read through `transcode.c` carefully.
+ */
+int rb_econv_decorate_at_first(rb_econv_t *ec, const char *decorator_name);
+
+/**
+ * Identical to rb_econv_decorate_at_first(), except it adds to the opposite
+ * direction. For instance CRLF conversion would run _after_ encodings are
+ * converted.
+ *
+ * @param[out] ec Target converter to decorate.
+ * @param[in] decorator_name Name of decorator to prepend.
+ * @retval 0 Success.
+ * @retval -1 Failure (no such decorator etc.).
+ * @post Decorator works after encoding conversion happens.
+ */
+int rb_econv_decorate_at_last(rb_econv_t *ec, const char *decorator_name);
+
+/**
+ * Creates a `rb_eConverterNotFoundError` exception object (but does not
+ * raise).
+ *
+ * @param[in] senc Name of source encoding.
+ * @param[in] denc Name of destination encoding.
+ * @param[in] ecflags A set of enum ::ruby_econv_flag_type.
+ * @return An instance of `rb_eConverterNotFoundError`.
+ */
+VALUE rb_econv_open_exc(const char *senc, const char *denc, int ecflags);
+
+/**
+ * Appends the passed string to the passed converter's output buffer. This can
+ * be handy when an encoding needs bytes out of thin air; for instance
+ * ISO-2022-JP has "shift function" which does not correspond to any
+ * characters.
+ *
+ * @param[out] ec Target converter.
+ * @param[in] str String to insert.
+ * @param[in] len Number of bytes of `str`.
+ * @param[in] str_encoding Encoding of `str`.
+ * @retval 0 Success.
+ * @retval -1 Failure (conversion error etc.).
+ * @note `str_encoding` can be anything, and `str` itself is converted
+ * when necessary.
+ */
+int rb_econv_insert_output(rb_econv_t *ec,
+ const unsigned char *str, size_t len, const char *str_encoding);
+
+/**
+ * Queries an encoding name which best suits for rb_econv_insert_output()'s
+ * last parameter. Strings in this encoding need no conversion when inserted;
+ * can be both time/space efficient.
+ *
+ * @param[in] ec Target converter.
+ * @return Its encoding for insertion.
+ */
+const char *rb_econv_encoding_to_insert_output(rb_econv_t *ec);
+
+/**
+ * This is a rb_econv_make_exception() + rb_exc_raise() combo.
+ *
+ * @param[in] ec (Possibly failed) conversion.
+ * @exception rb_eInvalidByteSequenceError Invalid byte sequence.
+ * @exception rb_eUndefinedConversionError Conversion undefined.
+ * @note This function can return when no error.
+ */
+void rb_econv_check_error(rb_econv_t *ec);
+
+/**
+ * This function makes sense right after rb_econv_convert() returns. As listed
+ * in ::rb_econv_result_t, rb_econv_convert() can bail out for various reasons.
+ * This function checks the passed converter's internal state and convert it to
+ * an appropriate exception object.
+ *
+ * @param[in] ec Target converter.
+ * @retval RUBY_Qnil The converter has no error.
+ * @retval otherwise Conversion error turned into an exception.
+ */
+VALUE rb_econv_make_exception(rb_econv_t *ec);
+
+/**
+ * Queries if rb_econv_putback() makes sense, i.e. there are invalid byte
+ * sequences remain in the buffer.
+ *
+ * @param[in] ec Target converter.
+ * @return Number of bytes that can be pushed back.
+ */
+int rb_econv_putbackable(rb_econv_t *ec);
+
+/**
+ * Puts back the bytes. In case of ::econv_invalid_byte_sequence, some of
+ * those invalid bytes are discarded and the others are buffered to be
+ * converted later. The latter bytes can be put back using this API.
+ *
+ * @param[out] ec Target converter (invalid byte sequence).
+ * @param[out] p Return buffer.
+ * @param[in] n Max number of bytes to put back.
+ * @post At most `n` bytes of what was put back is written to `p`.
+ */
+void rb_econv_putback(rb_econv_t *ec, unsigned char *p, int n);
+
+/**
+ * Queries the passed encoding's corresponding ASCII compatible encoding. "The
+ * corresponding ASCII compatible encoding" in this context is an ASCII
+ * compatible encoding which can represent exactly the same character sets as
+ * the given ASCII incompatible encoding. For instance that of UTF-16LE is
+ * UTF-8.
+ *
+ * @param[in] encname Name of an ASCII incompatible encoding.
+ * @retval NULL `encname` is already ASCII compatible.
+ * @retval otherwise The corresponding ASCII compatible encoding.
+ */
+const char *rb_econv_asciicompat_encoding(const char *encname);
+
+/**
+ * Identical to rb_econv_convert(), except it takes Ruby's string instead of
+ * C's pointer.
+ *
+ * @param[in,out] ec Target converter.
+ * @param[in] src Source string.
+ * @param[in] flags Flags (see rb_econv_convert).
+ * @exception rb_eArgError Converted string is too long.
+ * @exception rb_eInvalidByteSequenceError Invalid byte sequence.
+ * @exception rb_eUndefinedConversionError Conversion undefined.
+ * @return The conversion result.
+ */
+VALUE rb_econv_str_convert(rb_econv_t *ec, VALUE src, int flags);
+
+/**
+ * Identical to rb_econv_str_convert(), except it converts only a part of the
+ * passed string. Can be handy when you for instance want to do line-buffered
+ * conversion.
+ *
+ * @param[in,out] ec Target converter.
+ * @param[in] src Source string.
+ * @param[in] byteoff Number of bytes to seek.
+ * @param[in] bytesize Number of bytes to read.
+ * @param[in] flags Flags (see rb_econv_convert).
+ * @exception rb_eArgError Converted string is too long.
+ * @exception rb_eInvalidByteSequenceError Invalid byte sequence.
+ * @exception rb_eUndefinedConversionError Conversion undefined.
+ * @return The conversion result.
+ */
+VALUE rb_econv_substr_convert(rb_econv_t *ec, VALUE src, long byteoff, long bytesize, int flags);
+
+/**
+ * Identical to rb_econv_str_convert(), except it appends the conversion result
+ * to the additionally passed string instead of creating a new string. It can
+ * also be seen as a routine identical to rb_econv_append(), except it takes a
+ * Ruby's string instead of C's pointer.
+ *
+ * @param[in,out] ec Target converter.
+ * @param[in] src Source string.
+ * @param[in] dst Return buffer.
+ * @param[in] flags Flags (see rb_econv_convert).
+ * @exception rb_eArgError Converted string is too long.
+ * @exception rb_eInvalidByteSequenceError Invalid byte sequence.
+ * @exception rb_eUndefinedConversionError Conversion undefined.
+ * @return The conversion result.
+ */
+VALUE rb_econv_str_append(rb_econv_t *ec, VALUE src, VALUE dst, int flags);
+
+/**
+ * Identical to rb_econv_str_append(), except it appends only a part of the
+ * passed string with conversion. It can also be seen as a routine identical
+ * to rb_econv_substr_convert(), except it appends the conversion result to the
+ * additionally passed string instead of creating a new string.
+ *
+ * @param[in,out] ec Target converter.
+ * @param[in] src Source string.
+ * @param[in] byteoff Number of bytes to seek.
+ * @param[in] bytesize Number of bytes to read.
+ * @param[in] dst Return buffer.
+ * @param[in] flags Flags (see rb_econv_convert).
+ * @exception rb_eArgError Converted string is too long.
+ * @exception rb_eInvalidByteSequenceError Invalid byte sequence.
+ * @exception rb_eUndefinedConversionError Conversion undefined.
+ * @return The conversion result.
+ */
+VALUE rb_econv_substr_append(rb_econv_t *ec, VALUE src, long byteoff, long bytesize, VALUE dst, int flags);
+
+/**
+ * Converts the passed C's pointer according to the passed converter, then
+ * append the conversion result to the passed Ruby's string. This way buffer
+ * overflow is properly avoided to resize the destination properly.
+ *
+ * @param[in,out] ec Target converter.
+ * @param[in] bytesrc Target string.
+ * @param[in] bytesize Number of bytes of `bytesrc`.
+ * @param[in] dst Return buffer.
+ * @param[in] flags Flags (see rb_econv_convert).
+ * @exception rb_eArgError Converted string is too long.
+ * @exception rb_eInvalidByteSequenceError Invalid byte sequence.
+ * @exception rb_eUndefinedConversionError Conversion undefined.
+ * @return The conversion result.
+ */
+VALUE rb_econv_append(rb_econv_t *ec, const char *bytesrc, long bytesize, VALUE dst, int flags);
+
+/**
+ * This badly named function does not set the destination encoding to binary,
+ * but instead just nullifies newline conversion decorators if any. Other
+ * ordinal character conversions still happen after this; something non-binary
+ * would still be generated.
+ *
+ * @param[out] ec Target converter to modify.
+ * @post Any newline conversions, if any, would be killed.
+ */
+void rb_econv_binmode(rb_econv_t *ec);
+
+/**
+ * This enum is kind of omnibus. Gathers various constants.
+ */
+enum ruby_econv_flag_type {
+
+ /**
+ * @name Flags for rb_econv_open()
+ *
+ * @{
+ */
+
+ /** Mask for error handling related bits. */
+ RUBY_ECONV_ERROR_HANDLER_MASK = 0x000000ff,
+
+ /** Special handling of invalid sequences are there. */
+ RUBY_ECONV_INVALID_MASK = 0x0000000f,
+
+ /** Invalid sequences shall be replaced. */
+ RUBY_ECONV_INVALID_REPLACE = 0x00000002,
+
+ /** Special handling of undefined conversion are there. */
+ RUBY_ECONV_UNDEF_MASK = 0x000000f0,
+
+ /** Undefined characters shall be replaced. */
+ RUBY_ECONV_UNDEF_REPLACE = 0x00000020,
+
+ /** Undefined characters shall be escaped. */
+ RUBY_ECONV_UNDEF_HEX_CHARREF = 0x00000030,
+
+ /** Decorators are there. */
+ RUBY_ECONV_DECORATOR_MASK = 0x0001ff00,
+
+ /** Newline converters are there. */
+ RUBY_ECONV_NEWLINE_DECORATOR_MASK = 0x00007f00,
+
+ /** (Unclear; seems unused). */
+ RUBY_ECONV_NEWLINE_DECORATOR_READ_MASK = 0x00000f00,
+
+ /** (Unclear; seems unused). */
+ RUBY_ECONV_NEWLINE_DECORATOR_WRITE_MASK = 0x00007000,
+
+ /** Universal newline mode. */
+ RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR = 0x00000100,
+
+ /** CR to CRLF conversion shall happen. */
+ RUBY_ECONV_CRLF_NEWLINE_DECORATOR = 0x00001000,
+
+ /** CRLF to CR conversion shall happen. */
+ RUBY_ECONV_CR_NEWLINE_DECORATOR = 0x00002000,
+
+ /** CRLF to LF conversion shall happen. */
+ RUBY_ECONV_LF_NEWLINE_DECORATOR = 0x00004000,
+
+ /** Texts shall be XML-escaped. */
+ RUBY_ECONV_XML_TEXT_DECORATOR = 0x00008000,
+
+ /** Texts shall be AttrValue escaped */
+ RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR = 0x00010000,
+
+ /** (Unclear; seems unused). */
+ RUBY_ECONV_STATEFUL_DECORATOR_MASK = 0x00f00000,
+
+ /** Texts shall be AttrValue escaped. */
+ RUBY_ECONV_XML_ATTR_QUOTE_DECORATOR = 0x00100000,
+
+ /** Newline decorator's default. */
+ RUBY_ECONV_DEFAULT_NEWLINE_DECORATOR =
+#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
+ RUBY_ECONV_CRLF_NEWLINE_DECORATOR,
+#else
+ 0,
+#endif
+
+#define ECONV_ERROR_HANDLER_MASK RUBY_ECONV_ERROR_HANDLER_MASK /**< @old{RUBY_ECONV_ERROR_HANDLER_MASK} */
+#define ECONV_INVALID_MASK RUBY_ECONV_INVALID_MASK /**< @old{RUBY_ECONV_INVALID_MASK} */
+#define ECONV_INVALID_REPLACE RUBY_ECONV_INVALID_REPLACE /**< @old{RUBY_ECONV_INVALID_REPLACE} */
+#define ECONV_UNDEF_MASK RUBY_ECONV_UNDEF_MASK /**< @old{RUBY_ECONV_UNDEF_MASK} */
+#define ECONV_UNDEF_REPLACE RUBY_ECONV_UNDEF_REPLACE /**< @old{RUBY_ECONV_UNDEF_REPLACE} */
+#define ECONV_UNDEF_HEX_CHARREF RUBY_ECONV_UNDEF_HEX_CHARREF /**< @old{RUBY_ECONV_UNDEF_HEX_CHARREF} */
+#define ECONV_DECORATOR_MASK RUBY_ECONV_DECORATOR_MASK /**< @old{RUBY_ECONV_DECORATOR_MASK} */
+#define ECONV_NEWLINE_DECORATOR_MASK RUBY_ECONV_NEWLINE_DECORATOR_MASK /**< @old{RUBY_ECONV_NEWLINE_DECORATOR_MASK} */
+#define ECONV_NEWLINE_DECORATOR_READ_MASK RUBY_ECONV_NEWLINE_DECORATOR_READ_MASK /**< @old{RUBY_ECONV_NEWLINE_DECORATOR_READ_MASK} */
+#define ECONV_NEWLINE_DECORATOR_WRITE_MASK RUBY_ECONV_NEWLINE_DECORATOR_WRITE_MASK /**< @old{RUBY_ECONV_NEWLINE_DECORATOR_WRITE_MASK} */
+#define ECONV_UNIVERSAL_NEWLINE_DECORATOR RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_UNIVERSAL_NEWLINE_DECORATOR} */
+#define ECONV_CRLF_NEWLINE_DECORATOR RUBY_ECONV_CRLF_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_CRLF_NEWLINE_DECORATOR} */
+#define ECONV_CR_NEWLINE_DECORATOR RUBY_ECONV_CR_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_CR_NEWLINE_DECORATOR} */
+#define ECONV_LF_NEWLINE_DECORATOR RUBY_ECONV_LF_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_LF_NEWLINE_DECORATOR} */
+#define ECONV_XML_TEXT_DECORATOR RUBY_ECONV_XML_TEXT_DECORATOR /**< @old{RUBY_ECONV_XML_TEXT_DECORATOR} */
+#define ECONV_XML_ATTR_CONTENT_DECORATOR RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR /**< @old{RUBY_ECONV_XML_ATTR_CONTENT_DECORATOR} */
+#define ECONV_STATEFUL_DECORATOR_MASK RUBY_ECONV_STATEFUL_DECORATOR_MASK /**< @old{RUBY_ECONV_STATEFUL_DECORATOR_MASK} */
+#define ECONV_XML_ATTR_QUOTE_DECORATOR RUBY_ECONV_XML_ATTR_QUOTE_DECORATOR /**< @old{RUBY_ECONV_XML_ATTR_QUOTE_DECORATOR} */
+#define ECONV_DEFAULT_NEWLINE_DECORATOR RUBY_ECONV_DEFAULT_NEWLINE_DECORATOR /**< @old{RUBY_ECONV_DEFAULT_NEWLINE_DECORATOR} */
+ /** @} */
+
+ /**
+ * @name Flags for rb_econv_convert()
+ *
+ * @{
+ */
+
+ /** Indicates the input is a part of much larger one. */
+ RUBY_ECONV_PARTIAL_INPUT = 0x00020000,
+
+ /** Instructs the converter to stop after output. */
+ RUBY_ECONV_AFTER_OUTPUT = 0x00040000,
+#define ECONV_PARTIAL_INPUT RUBY_ECONV_PARTIAL_INPUT /**< @old{RUBY_ECONV_PARTIAL_INPUT} */
+#define ECONV_AFTER_OUTPUT RUBY_ECONV_AFTER_OUTPUT /**< @old{RUBY_ECONV_AFTER_OUTPUT} */
+
+ RUBY_ECONV_FLAGS_PLACEHOLDER /**< Placeholder (not used) */
+};
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_INTERNAL_ENCODING_TRANSCODE_H */
diff --git a/include/ruby/internal/error.h b/include/ruby/internal/error.h
new file mode 100644
index 0000000000..5bf82bfe7d
--- /dev/null
+++ b/include/ruby/internal/error.h
@@ -0,0 +1,599 @@
+#ifndef RBIMPL_ERROR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ERROR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Declares ::rb_raise().
+ */
+#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/format.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+/**
+ * @defgroup exception Exception handlings
+ * @{
+ */
+
+/**
+ * Warning categories. A warning issued using this API can be selectively
+ * requested / suppressed by the end-users. For instance passing
+ * `-W:no-deprecated` to the ruby process would suppress those warnings in
+ * deprecated category.
+ *
+ * @warning There is no way to declare a new category (for now).
+ */
+typedef enum {
+ /** Category unspecified. */
+ RB_WARN_CATEGORY_NONE,
+
+ /** Warning is for deprecated features. */
+ RB_WARN_CATEGORY_DEPRECATED,
+
+ /** Warning is for experimental features. */
+ RB_WARN_CATEGORY_EXPERIMENTAL,
+
+ /** Warning is for performance issues (not enabled by -w). */
+ RB_WARN_CATEGORY_PERFORMANCE,
+
+ /** Warning is for checking unused block strictly */
+ RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK,
+
+ RB_WARN_CATEGORY_DEFAULT_BITS = (
+ (1U << RB_WARN_CATEGORY_DEPRECATED) |
+ (1U << RB_WARN_CATEGORY_EXPERIMENTAL) |
+ 0),
+
+ RB_WARN_CATEGORY_ALL_BITS = (
+ (1U << RB_WARN_CATEGORY_DEPRECATED) |
+ (1U << RB_WARN_CATEGORY_EXPERIMENTAL) |
+ (1U << RB_WARN_CATEGORY_PERFORMANCE) |
+ (1U << RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK) |
+ 0)
+} rb_warning_category_t;
+
+/** for rb_readwrite_sys_fail first argument */
+enum rb_io_wait_readwrite {RB_IO_WAIT_READABLE, RB_IO_WAIT_WRITABLE};
+/** @cond INTERNAL_MACRO */
+#define RB_IO_WAIT_READABLE RB_IO_WAIT_READABLE
+#define RB_IO_WAIT_WRITABLE RB_IO_WAIT_WRITABLE
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * This is the same as `$!` in Ruby.
+ *
+ * @retval RUBY_Qnil Not handling exceptions at the moment.
+ * @retval otherwise The current exception in the current thread.
+ * @ingroup exception
+ */
+VALUE rb_errinfo(void);
+
+/**
+ * Sets the current exception (`$!`) to the given value.
+ *
+ * @param[in] err An instance of ::rb_eException, or ::RUBY_Qnil.
+ * @exception rb_eTypeError What is given was neither ::rb_eException nor
+ * ::RUBY_Qnil.
+ * @note Use rb_raise() instead to raise `err`. This function just
+ * assigns the given object to the global variable.
+ * @ingroup exception
+ */
+void rb_set_errinfo(VALUE err);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Exception entry point. By calling this function the execution of your
+ * program gets interrupted to "raise" an exception up to the callee entities.
+ * Programs could "rescue" that exception, or could "ensure" some part of them.
+ * If nobody cares about such things, the raised exception reaches at the top
+ * of execution. This yields abnormal end of the process.
+ *
+ * @param[in] exc A subclass of ::rb_eException.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @exception exc The specified exception.
+ * @note It never returns.
+ */
+void rb_raise(VALUE exc, const char *fmt, ...);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+/**
+ * Raises the unsung "fatal" exception. This is considered severe. Nobody can
+ * rescue the exception. Once raised, process termination is inevitable.
+ * However ensure clauses still run, so that resources are properly cleaned up.
+ *
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @exception rb_eFatal An exception that you cannot rescue.
+ * @note It never returns.
+ */
+void rb_fatal(const char *fmt, ...);
+
+RBIMPL_ATTR_COLD()
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+/**
+ * Interpreter panic switch. Immediate process termination without any
+ * synchronisations shall occur. LOTS of internal states, stack traces, and
+ * even machine registers are displayed if possible for debugging purposes
+ * then.
+ *
+ * @warning Do not use this API.
+ * @warning You are not expected to use this API.
+ * @warning Why not just fix your code instead of calling this API?
+ * @warning It was a bad idea to expose this API to extension libraries at
+ * the first place. We just cannot delete it at this point for
+ * backwards compatibility. That doesn't mean everyone are
+ * welcomed to call this function at will.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @note It never returns.
+ */
+void rb_bug(const char *fmt, ...);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is a wrapper of rb_bug() which automatically constructs appropriate
+ * message from the passed errno.
+ *
+ * @param[in] msg Additional message to display.
+ * @exception err C level errno.
+ * @note It never returns.
+ */
+void rb_bug_errno(const char *msg, int err);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Converts a C errno into a Ruby exception, then raises it. For instance:
+ *
+ * ```CXX
+ * static VALUE
+ * foo(VALUE argv)
+ * {
+ * const auto cmd = StringValueCStr(argv);
+ * const auto waitr = system(cmd);
+ * if (waitr == -1) {
+ * rb_sys_fail("system(3posix)"); // <-------------- this
+ * }
+ * else {
+ * return INT2FIX(fd);
+ * }
+ * }
+ * ```
+ *
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eSystemCallError An exception representing errno.
+ * @note It never returns.
+ */
+void rb_sys_fail(const char *msg);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_sys_fail(), except it takes the message in Ruby's String
+ * instead of C's.
+ *
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eSystemCallError An exception representing errno.
+ * @note It never returns.
+ */
+void rb_sys_fail_str(VALUE msg);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * Identical to rb_sys_fail(), except it takes additional module to extend the
+ * exception object before raising.
+ *
+ * @param[in] mod A ::rb_cModule instance.
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eSystemCallError An exception representing errno.
+ * @note It never returns.
+ *
+ * @internal
+ *
+ * Does anybody use it?
+ */
+void rb_mod_sys_fail(VALUE mod, const char *msg);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_mod_sys_fail(), except it takes the message in Ruby's String
+ * instead of C's.
+ *
+ * @param[in] mod A ::rb_cModule instance.
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eSystemCallError An exception representing errno.
+ * @note It never returns.
+ */
+void rb_mod_sys_fail_str(VALUE mod, VALUE msg);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Raises appropriate exception using the parameters.
+ *
+ * In Ruby level there are rb_eEAGAINWaitReadable etc. This function maps the
+ * given parameter to an appropriate exception class, then raises it.
+ *
+ * @param[in] waiting Reason for the IO to wait.
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eEAGAINWaitWritable
+ * @exception rb_eEWOULDBLOCKWaitWritable
+ * @exception rb_eEINPROGRESSWaitWritable
+ * @exception rb_eEAGAINWaitReadable
+ * @exception rb_eEWOULDBLOCKWaitReadable
+ * @exception rb_eEINPROGRESSWaitReadable
+ * @exception rb_eSystemCallError
+ * @note It never returns.
+ */
+void rb_readwrite_sys_fail(enum rb_io_wait_readwrite waiting, const char *msg);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Breaks from a block. Because you are using a CAPI this is not as intuitive
+ * as it sounds. In order for this function to properly work, make a
+ * ::rb_block_call_func_t function that calls it internally, and pass that
+ * function to rb_block_call().
+ *
+ * @exception rb_eLocalJumpError Called from outside of a block.
+ * @note It never returns.
+ */
+void rb_iter_break(void);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_iter_break(), except it additionally takes the "value" of
+ * this breakage. It will be the evaluation result of the iterator. This is
+ * kind of complicated; you cannot see this as a "return from a block"
+ * behaviour. Take a look at this example:
+ *
+ * ```ruby
+ * def foo(q)
+ * puts(w = yield(q))
+ * puts(e = yield(w))
+ * puts(r = yield(e))
+ * puts(t = yield(r))
+ * puts(y = yield(t))
+ * return "howdy!"
+ * end
+ *
+ * x = foo(0) {|i|
+ * if i > 2
+ * break "hello!"
+ * else
+ * next i + 1
+ * end
+ * }
+ *
+ * puts x
+ * ```
+ *
+ * This script outputs 1, 2, 3, and hello. Note that the value passed to break
+ * becomes the return value of foo method, not the value of yield. This is
+ * confusing, but can be handy on occasions e.g. when you want to bring a
+ * local variable out of a block.
+ *
+ * @param[in] val The value of the iterator.
+ * @exception rb_eLocalJumpError Called from outside of a block.
+ * @note It never returns.
+ */
+void rb_iter_break_value(VALUE val);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Terminates the current execution context. This API is the entry point of a
+ * "well-mannered" termination sequence. When called from an extension
+ * library, it raises ::rb_eSystemExit exception. Programs could rescue that
+ * exception. Can cancel process exit then. Otherwise, that exception results
+ * in a process termination with the status passed to this function.
+ *
+ * @param[in] status Exit status, see also exit(3).
+ * @exception rb_eSystemExit Exception representing the exit status.
+ * @note It never returns.
+ *
+ * @internal
+ *
+ * "When called from an extension library"? You might wonder. In fact there
+ * are chances for this function to be called from outside of it, for instance
+ * when dlopen(3) failed. In case it is not possible for this function to
+ * raise an exception, it does not (silently enters to process cleanup). But
+ * that is a kind of implementation detail which extension library authors
+ * should not bother.
+ */
+void rb_exit(int status);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * @exception rb_eNotImpError
+ * @note It never returns.
+ */
+void rb_notimplement(void);
+
+/**
+ * Creates an exception object that represents the given C errno.
+ *
+ * @param[in] err C level errno.
+ * @param[in] msg Additional message.
+ * @retval rb_eSystemCallError An exception for the errno.
+ */
+VALUE rb_syserr_new(int err, const char * msg);
+
+/**
+ * Identical to rb_syserr_new(), except it takes the message in Ruby's String
+ * instead of C's.
+ *
+ * @param[in] n C level errno.
+ * @param[in] arg Additional message.
+ * @retval rb_eSystemCallError An exception for the errno.
+ */
+VALUE rb_syserr_new_str(int n, VALUE arg);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Raises appropriate exception that represents a C errno.
+ *
+ * @param[in] err C level errno.
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eSystemCallError An exception representing `err`.
+ * @note It never returns.
+ */
+void rb_syserr_fail(int err, const char *msg);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_syserr_fail(), except it takes the message in Ruby's String
+ * instead of C's.
+ *
+ * @param[in] err C level errno.
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eSystemCallError An exception representing `err`.
+ * @note It never returns.
+ */
+void rb_syserr_fail_str(int err, VALUE msg);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_mod_sys_fail(), except it does not depend on C global
+ * variable errno. Pass it explicitly.
+ *
+ * @param[in] mod A ::rb_cModule instance.
+ * @param[in] err C level errno.
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eSystemCallError An exception representing `err`.
+ * @note It never returns.
+ */
+void rb_mod_syserr_fail(VALUE mod, int err, const char *msg);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_mod_syserr_fail(), except it takes the message in Ruby's
+ * String instead of C's.
+ *
+ * @param[in] mod A ::rb_cModule instance.
+ * @param[in] err C level errno.
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eSystemCallError An exception representing `err`.
+ * @note It never returns.
+ */
+void rb_mod_syserr_fail_str(VALUE mod, int err, VALUE msg);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_readwrite_sys_fail(), except it does not depend on C global
+ * variable errno. Pass it explicitly.
+ *
+ * @param[in] waiting Reason for the IO to wait.
+ * @param[in] err C level errno.
+ * @param[in] msg Additional message to raise.
+ * @exception rb_eEAGAINWaitWritable
+ * @exception rb_eEWOULDBLOCKWaitWritable
+ * @exception rb_eEINPROGRESSWaitWritable
+ * @exception rb_eEAGAINWaitReadable
+ * @exception rb_eEWOULDBLOCKWaitReadable
+ * @exception rb_eEINPROGRESSWaitReadable
+ * @exception rb_eSystemCallError
+ * @note It never returns.
+ */
+void rb_readwrite_syserr_fail(enum rb_io_wait_readwrite waiting, int err, const char *msg);
+
+RBIMPL_ATTR_COLD()
+RBIMPL_ATTR_NORETURN()
+/**
+ * @private
+ *
+ * Fails with the given object's type incompatibility to the type.
+ *
+ * This is an implementation detail of Check_Type. People don't use it
+ * directly.
+ *
+ * @param[in] self The object in question.
+ * @param[in] t Expected type of the object.
+ * @exception rb_eTypeError `self` not in type `t`.
+ * @note It never returns.
+ * @note The second argument must have been an enum ::ruby_value_type,
+ * but for historical reasons it remains to be an int (in other
+ * words we see no benefits fixing this bug).
+ */
+void rb_unexpected_type(VALUE self, int t);
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #ruby_verbose. Please don't use it
+ * directly.
+ *
+ * @retval Qnil Interpreter is quiet.
+ * @retval Qfalse Interpreter is kind of chatty.
+ * @retval otherwise Interpreter is very verbose.
+ */
+VALUE *rb_ruby_verbose_ptr(void);
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #ruby_debug. Please don't use it
+ * directly.
+ *
+ * @retval Qnil Interpreter not in debug mode.
+ * @retval Qfalse Interpreter not in debug mode.
+ * @retval otherwise Interpreter is in debug mode.
+ */
+VALUE *rb_ruby_debug_ptr(void);
+
+/**
+ * This variable controls whether the interpreter is in debug mode. Setting
+ * this to some truthy value is equivalent to passing `-W` flag to the
+ * interpreter. Setting this to ::Qfalse is equivalent to passing `-W1` flag
+ * to the interpreter. Setting this to ::Qnil is equivalent to passing `-W0`
+ * flag to the interpreter.
+ *
+ * @retval Qnil Interpreter is quiet.
+ * @retval Qfalse Interpreter is kind of chatty.
+ * @retval otherwise Interpreter is very verbose.
+ */
+#define ruby_verbose (*rb_ruby_verbose_ptr())
+
+/**
+ * This variable controls whether the interpreter is in debug mode. Setting
+ * this to some truthy value is equivalent to passing `-d` flag to the
+ * interpreter.
+ *
+ * @retval Qnil Interpreter not in debug mode.
+ * @retval Qfalse Interpreter not in debug mode.
+ * @retval otherwise Interpreter is in debug mode.
+ */
+#define ruby_debug (*rb_ruby_debug_ptr())
+
+/* reports if $VERBOSE is true */
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+/**
+ * Issues a warning.
+ *
+ * In ruby, warnings these days are tightly coupled with the rb_mWarning
+ * constant and its `warn` singleton method. This CAPI is just a thin wrapper
+ * of it; everything passed are formatted like what rb_sprintf() does, then
+ * passed through to the method. Programs can have their own `def
+ * Warning.warn` at will to do whatever they want, from ignoring the warnings
+ * at all to sinking them to some BigQuery data set via a Fluentd cluster. By
+ * default, the method just emits its passed contents to ::rb_stderr using
+ * rb_io_write().
+ *
+ * @note This function is affected by the value of $VERBOSE, it does
+ * nothing unless $VERBOSE is true.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+void rb_warning(const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Identical to rb_warning(), except it takes additional "category" parameter.
+ *
+ * @param[in] cat Name of a known category.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ */
+void rb_category_warning(rb_warning_category_t cat, const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((1, 3))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
+/**
+ * Issues a compile-time warning that happens at `__file__:__line__`. Purpose
+ * of this function being exposed to CAPI is unclear.
+ *
+ * @note This function is affected by the value of $VERBOSE.
+ * @param[in] file The path corresponding to Ruby level `__FILE__`.
+ * @param[in] line The number corresponding to Ruby level `__LINE__`.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ */
+void rb_compile_warning(const char *file, int line, const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+/**
+ * Identical to rb_sys_fail(), except it does not raise an exception to render
+ * a warning instead.
+ *
+ * @note This function is affected by the value of $VERBOSE.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ */
+void rb_sys_warning(const char *fmt, ...);
+
+/* reports if $VERBOSE is not nil (so if it is true or false) */
+RBIMPL_ATTR_COLD()
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+/**
+ * Identical to rb_warning(), except it reports unless $VERBOSE is nil.
+ *
+ * @note This function is affected by the value of $VERBOSE, it does
+ * nothing if $VERBOSE is nil.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ */
+void rb_warn(const char *fmt, ...);
+
+RBIMPL_ATTR_COLD()
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Identical to rb_category_warning(), except it reports unless $VERBOSE is nil.
+ *
+ * @param[in] cat Category e.g. deprecated.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ */
+void rb_category_warn(rb_warning_category_t cat, const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((1, 3))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
+/**
+ * Identical to rb_compile_warning(), except it reports unless $VERBOSE is nil.
+ *
+ * @param[in] file The path corresponding to Ruby level `__FILE__`.
+ * @param[in] line The number corresponding to Ruby level `__LINE__`.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ */
+void rb_compile_warn(const char *file, int line, const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((2, 4))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 4, 5)
+/**
+ * Identical to rb_compile_warn(), except it also accepts category.
+ *
+ * @param[in] cat Category e.g. deprecated.
+ * @param[in] file The path corresponding to Ruby level `__FILE__`.
+ * @param[in] line The number corresponding to Ruby level `__LINE__`.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ */
+void rb_category_compile_warn(rb_warning_category_t cat, const char *file, int line, const char *fmt, ...);
+
+/** @} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_ERROR_H */
diff --git a/include/ruby/internal/eval.h b/include/ruby/internal/eval.h
new file mode 100644
index 0000000000..23aa1d9580
--- /dev/null
+++ b/include/ruby/internal/eval.h
@@ -0,0 +1,405 @@
+#ifndef RBIMPL_EVAL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_EVAL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Declares ::rb_eval_string().
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Evaluates the given string.
+ *
+ * In case it is called from within a C-backended method, the evaluation is
+ * done under the current binding. However there can be no method. On such
+ * situation this function evaluates in an isolated binding, like `require`
+ * runs in a separate one.
+ *
+ * `__FILE__` will be `"(eval)"`, and `__LINE__` starts from 1 in the
+ * evaluation.
+ *
+ * @param[in] str Ruby code to evaluate.
+ * @exception rb_eException Raises an exception on error.
+ * @return The evaluated result.
+ *
+ * @internal
+ *
+ * @shyouhei's old tale about the birth and growth of this function:
+ *
+ * At the beginning, there was no rb_eval_string(). @shyouhei heard that
+ * @shugo, author of Apache httpd's mod_ruby module, requested @matz for this
+ * API. He wanted a way so that mod_ruby can evaluate ruby scripts one by one,
+ * separately, in each different contexts. So this function was made. It was
+ * designed to be a global interpreter entry point like ruby_run_node().
+ *
+ * The way it is implemented however allows extension libraries (not just
+ * programs like Apache httpd) to call this function. Because its name says
+ * nothing about the initial design, people started to think of it as an
+ * orthodox way to call ruby level `eval` method from their extension
+ * libraries. Even our `extension.rdoc` has had a description of this function
+ * basically according to this understanding.
+ *
+ * The old (mod_ruby like) usage still works. But over time, usages of this
+ * function from extension libraries got popular, while mod_ruby faded out; is
+ * no longer maintained now. Devs decided to actively support both. This
+ * function now auto-detects how it is called, and switches how it works
+ * depending on it.
+ *
+ * @see https://bugs.ruby-lang.org/issues/18780
+ */
+VALUE rb_eval_string(const char *str);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_eval_string(), except it avoids potential global escapes.
+ * Such global escapes include exceptions, `throw`, `break`, for example.
+ *
+ * It first evaluates the given string as rb_eval_string() does. If no global
+ * escape occurred during the evaluation, it returns the result and `*state` is
+ * zero. Otherwise, it returns some undefined value and sets `*state` to
+ * nonzero. If state is `NULL`, it is not set in both cases.
+ *
+ * @param[in] str Ruby code to evaluate.
+ * @param[out] state State of execution.
+ * @return The evaluated result if succeeded, an undefined value if
+ * otherwise.
+ * @post `*state` is set to zero if succeeded. Nonzero otherwise.
+ * @warning You have to clear the error info with `rb_set_errinfo(Qnil)` if
+ * you decide to ignore the caught exception.
+ * @see rb_eval_string
+ * @see rb_protect
+ *
+ * @internal
+ *
+ * The "undefined value" described above is in fact ::RUBY_Qnil for now. But
+ * @shyouhei doesn't think that we would never change that.
+ *
+ * Though not a part of our public API, `state` is in fact an
+ * enum ruby_tag_type. You can see the potential "nonzero" values by looking
+ * at vm_core.h.
+ */
+VALUE rb_eval_string_protect(const char *str, int *state);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_eval_string_protect(), except it evaluates the given string
+ * under a module binding in an isolated binding. This is the same as a
+ * binding for loaded libraries on `rb_load(something, true)`.
+ *
+ * @param[in] str Ruby code to evaluate.
+ * @param[out] state State of execution.
+ * @return The evaluated result if succeeded, an undefined value if
+ * otherwise.
+ * @post `*state` is set to zero if succeeded. Nonzero otherwise.
+ * @warning You have to clear the error info with `rb_set_errinfo(Qnil)` if
+ * you decide to ignore the caught exception.
+ * @see rb_eval_string
+ */
+VALUE rb_eval_string_wrap(const char *str, int *state);
+
+/**
+ * Calls a method. Can call both public and private methods.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] n Number of arguments that follow.
+ * @param[in] ... Arbitrary number of method arguments.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ */
+VALUE rb_funcall(VALUE recv, ID mid, int n, ...);
+
+/**
+ * Identical to rb_funcall(), except it takes the method arguments as a C
+ * array.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ */
+VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_funcallv(), except you can specify how to handle the last
+ * element of the given array.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ */
+VALUE rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat);
+
+/**
+ * Identical to rb_funcallv(), except it only takes public methods into
+ * account. This is roughly Ruby's `Object#public_send`.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eNoMethodError The method is private or protected.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ */
+VALUE rb_funcallv_public(VALUE recv, ID mid, int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_funcallv_public(), except you can specify how to handle the
+ * last element of the given array. It can also be seen as a routine identical
+ * to rb_funcallv_kw(), except it only takes public methods into account.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eNoMethodError The method is private or protected.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ */
+VALUE rb_funcallv_public_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat);
+
+/**
+ * @deprecated This is an old name of rb_funcallv(). Provided here for
+ * backwards compatibility to 2.x programs (introduced in 2.1).
+ * It is not a good name. Please don't use it any longer.
+ */
+#define rb_funcall2 rb_funcallv
+
+/**
+ * @deprecated This is an old name of rb_funcallv_public(). Provided here
+ * for backwards compatibility to 2.x programs (introduced in
+ * 2.1). It is not a good name. Please don't use it any longer.
+ */
+#define rb_funcall3 rb_funcallv_public
+
+/**
+ * Identical to rb_funcallv_public(), except you can pass the passed block.
+ *
+ * Sometimes you want to "pass" a block parameter form one method to another.
+ * Suppose you have this Ruby method `foo`:
+ *
+ * ```ruby
+ * def foo(x, y, &z)
+ * x.open(y, &z)
+ * end
+ * ```
+ *
+ * And suppose you want to translate this into C. Then
+ * rb_funcall_passing_block() function is usable in this situation.
+ *
+ * ```CXX
+ * VALUE
+ * foo_translated_into_C(VALUE self, VALUE x, VALUE y)
+ * {
+ * const auto open = rb_intern("open");
+ *
+ * return rb_funcall_passing_block(x, open, 1, &y);
+ * }
+ * ```
+ *
+ * @see rb_yield_block
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eNoMethodError The method is private or protected.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ */
+VALUE rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_funcallv_passing_block(), except you can specify how to
+ * handle the last element of the given array. It can also be seen as a
+ * routine identical to rb_funcallv_public_kw(), except you can pass the passed
+ * block.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eNoMethodError The method is private or protected.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ */
+VALUE rb_funcall_passing_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat);
+
+/**
+ * Identical to rb_funcallv_public(), except you can pass a block. A block
+ * here basically is an instance of ::rb_cProc. If you want to exercise
+ * `to_proc` conversion, do so before passing it here. However nil and symbols
+ * are special-case allowed.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] procval An instance of Proc, Symbol, or NilClass.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eNoMethodError The method is private or protected.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ *
+ * @internal
+ *
+ * Implementation-wise, `procval` is in fact a "block handler" object. You
+ * could also pass an IFUNC (block_handler_ifunc) here to say precise. --- But
+ * AFAIK there is no 3rd party way to even know that there are objects called
+ * IFUNC behind-the-scene.
+ */
+VALUE rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE procval);
+
+/**
+ * Identical to rb_funcallv_with_block(), except you can specify how to handle
+ * the last element of the given array. It can also be seen as a routine
+ * identical to rb_funcallv_public_kw(), except you can pass a block.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] procval An instance of Proc, Symbol, or NilClass.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eNoMethodError The method is private or protected.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ */
+VALUE rb_funcall_with_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE procval, int kw_splat);
+
+/**
+ * This resembles ruby's `super`.
+ *
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @exception rb_eNoMethodError No super method are there.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the super method evaluates to.
+ */
+VALUE rb_call_super(int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_call_super(), except you can specify how to handle the last
+ * element of the given array.
+ *
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eNoMethodError No super method are there.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the super method evaluates to.
+ */
+VALUE rb_call_super_kw(int argc, const VALUE *argv, int kw_splat);
+
+/**
+ * This resembles ruby's `self`.
+ *
+ * @exception rb_eRuntimeError Called from outside of method context.
+ * @return Current receiver.
+ */
+VALUE rb_current_receiver(void);
+
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * Keyword argument deconstructor.
+ *
+ * Retrieves argument values bound to keywords, which directed by `table` into
+ * `values`, deleting retrieved entries from `keyword_hash` along the way.
+ * First `required` number of IDs referred by `table` are mandatory, and
+ * succeeding `optional` (`-optional-1` if `optional` is negative) number of
+ * IDs are optional. If a mandatory key is not contained in `keyword_hash`,
+ * raises ::rb_eArgError. If an optional key is not present in `keyword_hash`,
+ * the corresponding element in `values` is set to ::RUBY_Qundef. If
+ * `optional` is negative, rest of `keyword_hash` are ignored, otherwise raises
+ * ::rb_eArgError.
+ *
+ * @warning Handling keyword arguments in the C API is less efficient than
+ * handling them in Ruby. Consider using a Ruby wrapper method
+ * around a non-keyword C function.
+ * @see https://bugs.ruby-lang.org/issues/11339
+ * @param[out] keyword_hash Target hash to deconstruct.
+ * @param[in] table List of keywords that you are interested in.
+ * @param[in] required Number of mandatory keywords.
+ * @param[in] optional Number of optional keywords (can be negative).
+ * @param[out] values Buffer to be filled.
+ * @exception rb_eArgError Absence of a mandatory keyword.
+ * @exception rb_eArgError Found an unknown keyword.
+ * @return Number of found values that are stored into `values`.
+ */
+int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Splits a hash into two.
+ *
+ * Takes a hash of various keys, and split it into symbol-keyed parts and
+ * others. Symbol-keyed part becomes the return value. What remains are
+ * returned as a new hash object stored at the argument pointer.
+ *
+ * @param[in,out] orighash Pointer to a target hash to split.
+ * @return An extracted keyword hash.
+ * @post Upon successful return `orighash` points to another hash
+ * object, whose contents are the remainder of the operation.
+ * @note The argument hash object is not modified.
+ */
+VALUE rb_extract_keywords(VALUE *orighash);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_EVAL_H */
diff --git a/include/ruby/internal/event.h b/include/ruby/internal/event.h
new file mode 100644
index 0000000000..1d194ed618
--- /dev/null
+++ b/include/ruby/internal/event.h
@@ -0,0 +1,159 @@
+#ifndef RBIMPL_EVENT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_EVENT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Debugging and tracing APIs.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/* These macros are not enums because they are wider than int.*/
+
+/**
+ * @name Traditional set_trace_func events
+ *
+ * @{
+ */
+#define RUBY_EVENT_NONE 0x0000 /**< No events. */
+#define RUBY_EVENT_LINE 0x0001 /**< Encountered a new line. */
+#define RUBY_EVENT_CLASS 0x0002 /**< Encountered a new class. */
+#define RUBY_EVENT_END 0x0004 /**< Encountered an end of a class clause. */
+#define RUBY_EVENT_CALL 0x0008 /**< A method, written in Ruby, is called. */
+#define RUBY_EVENT_RETURN 0x0010 /**< Encountered a `return` statement. */
+#define RUBY_EVENT_C_CALL 0x0020 /**< A method, written in C, is called. */
+#define RUBY_EVENT_C_RETURN 0x0040 /**< Return from a method, written in C. */
+#define RUBY_EVENT_RAISE 0x0080 /**< Encountered a `raise` statement. */
+#define RUBY_EVENT_ALL 0x00ff /**< Bitmask of traditional events. */
+
+/** @} */
+
+/**
+ * @name TracePoint extended events
+ *
+ * @{
+ */
+#define RUBY_EVENT_B_CALL 0x0100 /**< Encountered an `yield` statement. */
+#define RUBY_EVENT_B_RETURN 0x0200 /**< Encountered a `next` statement. */
+#define RUBY_EVENT_THREAD_BEGIN 0x0400 /**< Encountered a new thread. */
+#define RUBY_EVENT_THREAD_END 0x0800 /**< Encountered an end of a thread. */
+#define RUBY_EVENT_FIBER_SWITCH 0x1000 /**< Encountered a `Fiber#yield`. */
+#define RUBY_EVENT_SCRIPT_COMPILED 0x2000 /**< Encountered an `eval`. */
+#define RUBY_EVENT_RESCUE 0x4000 /**< Encountered a `rescue` statement. */
+#define RUBY_EVENT_TRACEPOINT_ALL 0xffff /**< Bitmask of extended events. */
+
+/** @} */
+
+/**
+ * @name Special events
+ *
+ * @internal
+ *
+ * These bits are actually used internally. See vm_core.h if you are curious.
+ *
+ * @endinternal
+ *
+ * @{
+ */
+#define RUBY_EVENT_RESERVED_FOR_INTERNAL_USE 0x030000 /**< Opaque bits. */
+
+/** @} */
+
+/**
+ * @name Internal events
+ *
+ * @shyouhei's understanding is that some of them are visible from extension
+ * libraries because of `ext/objspace`. But it seems that doesn't describe
+ * everything? The ultimate reason why they are here remains unclear.
+ *
+ * @{
+ */
+#define RUBY_INTERNAL_EVENT_SWITCH 0x040000 /**< Thread switched. */
+#define RUBY_EVENT_SWITCH 0x040000 /**< @old{RUBY_INTERNAL_EVENT_SWITCH} */
+ /* 0x080000 */
+#define RUBY_INTERNAL_EVENT_NEWOBJ 0x100000 /**< Object allocated. */
+#define RUBY_INTERNAL_EVENT_FREEOBJ 0x200000 /**< Object swept. */
+#define RUBY_INTERNAL_EVENT_GC_START 0x400000 /**< GC started. */
+#define RUBY_INTERNAL_EVENT_GC_END_MARK 0x800000 /**< GC ended mark phase. */
+#define RUBY_INTERNAL_EVENT_GC_END_SWEEP 0x1000000 /**< GC ended sweep phase. */
+#define RUBY_INTERNAL_EVENT_GC_ENTER 0x2000000 /**< `gc_enter()` is called. */
+#define RUBY_INTERNAL_EVENT_GC_EXIT 0x4000000 /**< `gc_exit()` is called. */
+#define RUBY_INTERNAL_EVENT_OBJSPACE_MASK 0x7f00000 /**< Bitmask of GC events. */
+#define RUBY_INTERNAL_EVENT_MASK 0xffff0000 /**< Bitmask of internal events. */
+
+/** @} */
+
+/**
+ * Represents event(s). As the name implies events are bit flags.
+ */
+typedef uint32_t rb_event_flag_t;
+
+/**
+ * Type of event hooks. When an event happens registered functions are kicked
+ * with appropriate parameters.
+ *
+ * @param[in] evflag The kind of event that happened.
+ * @param[in] data The `data` passed to rb_add_event_hook().
+ * @param[in] self Current receiver.
+ * @param[in] mid Name of the current method.
+ * @param[in] klass Current class.
+ */
+typedef void (*rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass);
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define RB_EVENT_HOOKS_HAVE_CALLBACK_DATA 1
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Registers an event hook function.
+ *
+ * @param[in] func A callback.
+ * @param[in] events A set of events that `func` should run.
+ * @param[in] data Passed as-is to `func`.
+ */
+void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data);
+
+/**
+ * Removes the passed function from the list of event hooks.
+ *
+ * @param[in] func A callback.
+ * @return Number of deleted event hooks.
+ * @note As multiple events can share the same `func` it is quite
+ * possible for the return value to become more than one.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't know if this is an Easter egg or an official feature, but
+ * you can pass 0 to the argument. That effectively swipes everything out from
+ * the hook list.
+ */
+int rb_remove_event_hook(rb_event_hook_func_t func);
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_EVENT_H */
diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h
new file mode 100644
index 0000000000..2afb3f1fa3
--- /dev/null
+++ b/include/ruby/internal/fl_type.h
@@ -0,0 +1,757 @@
+#ifndef RBIMPL_FL_TYPE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_FL_TYPE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines enum ::ruby_fl_type.
+ */
+#include "ruby/internal/config.h" /* for ENUM_OVER_INT */
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/flag_enum.h"
+#include "ruby/internal/attr/forceinline.h"
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/has/extension.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/internal/stdbool.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+#include "ruby/assert.h"
+#include "ruby/defines.h"
+
+/** @cond INTERNAL_MACRO */
+#if RBIMPL_HAS_EXTENSION(enumerator_attributes)
+# define RBIMPL_HAVE_ENUM_ATTRIBUTE 1
+#elif RBIMPL_COMPILER_SINCE(GCC, 6, 0, 0)
+# define RBIMPL_HAVE_ENUM_ATTRIBUTE 1
+#endif
+
+#ifdef ENUM_OVER_INT
+# define RBIMPL_WIDER_ENUM 1
+#elif SIZEOF_INT * CHAR_BIT > 12+19+1
+# define RBIMPL_WIDER_ENUM 1
+#else
+# define RBIMPL_WIDER_ENUM 0
+#endif
+/** @endcond */
+
+#define FL_SINGLETON RBIMPL_CAST((VALUE)RUBY_FL_SINGLETON) /**< @old{RUBY_FL_SINGLETON} */
+#define FL_WB_PROTECTED RBIMPL_CAST((VALUE)RUBY_FL_WB_PROTECTED) /**< @old{RUBY_FL_WB_PROTECTED} */
+#define FL_PROMOTED RBIMPL_CAST((VALUE)RUBY_FL_PROMOTED) /**< @old{RUBY_FL_PROMOTED} */
+#define FL_FINALIZE RBIMPL_CAST((VALUE)RUBY_FL_FINALIZE) /**< @old{RUBY_FL_FINALIZE} */
+#define FL_SHAREABLE RBIMPL_CAST((VALUE)RUBY_FL_SHAREABLE) /**< @old{RUBY_FL_SHAREABLE} */
+#define FL_UNTRUSTED RBIMPL_CAST((VALUE)RUBY_FL_UNTRUSTED) /**< @old{RUBY_FL_UNTRUSTED} */
+#define FL_EXIVAR RBIMPL_CAST((VALUE)RUBY_FL_EXIVAR) /**< @old{RUBY_FL_EXIVAR} */
+#define FL_FREEZE RBIMPL_CAST((VALUE)RUBY_FL_FREEZE) /**< @old{RUBY_FL_FREEZE} */
+
+#define FL_USHIFT RBIMPL_CAST((VALUE)RUBY_FL_USHIFT) /**< @old{RUBY_FL_USHIFT} */
+
+#define FL_USER0 RBIMPL_CAST((VALUE)RUBY_FL_USER0) /**< @old{RUBY_FL_USER0} */
+#define FL_USER1 RBIMPL_CAST((VALUE)RUBY_FL_USER1) /**< @old{RUBY_FL_USER1} */
+#define FL_USER2 RBIMPL_CAST((VALUE)RUBY_FL_USER2) /**< @old{RUBY_FL_USER2} */
+#define FL_USER3 RBIMPL_CAST((VALUE)RUBY_FL_USER3) /**< @old{RUBY_FL_USER3} */
+#define FL_USER4 RBIMPL_CAST((VALUE)RUBY_FL_USER4) /**< @old{RUBY_FL_USER4} */
+#define FL_USER5 RBIMPL_CAST((VALUE)RUBY_FL_USER5) /**< @old{RUBY_FL_USER5} */
+#define FL_USER6 RBIMPL_CAST((VALUE)RUBY_FL_USER6) /**< @old{RUBY_FL_USER6} */
+#define FL_USER7 RBIMPL_CAST((VALUE)RUBY_FL_USER7) /**< @old{RUBY_FL_USER7} */
+#define FL_USER8 RBIMPL_CAST((VALUE)RUBY_FL_USER8) /**< @old{RUBY_FL_USER8} */
+#define FL_USER9 RBIMPL_CAST((VALUE)RUBY_FL_USER9) /**< @old{RUBY_FL_USER9} */
+#define FL_USER10 RBIMPL_CAST((VALUE)RUBY_FL_USER10) /**< @old{RUBY_FL_USER10} */
+#define FL_USER11 RBIMPL_CAST((VALUE)RUBY_FL_USER11) /**< @old{RUBY_FL_USER11} */
+#define FL_USER12 RBIMPL_CAST((VALUE)RUBY_FL_USER12) /**< @old{RUBY_FL_USER12} */
+#define FL_USER13 RBIMPL_CAST((VALUE)RUBY_FL_USER13) /**< @old{RUBY_FL_USER13} */
+#define FL_USER14 RBIMPL_CAST((VALUE)RUBY_FL_USER14) /**< @old{RUBY_FL_USER14} */
+#define FL_USER15 RBIMPL_CAST((VALUE)RUBY_FL_USER15) /**< @old{RUBY_FL_USER15} */
+#define FL_USER16 RBIMPL_CAST((VALUE)RUBY_FL_USER16) /**< @old{RUBY_FL_USER16} */
+#define FL_USER17 RBIMPL_CAST((VALUE)RUBY_FL_USER17) /**< @old{RUBY_FL_USER17} */
+#define FL_USER18 RBIMPL_CAST((VALUE)RUBY_FL_USER18) /**< @old{RUBY_FL_USER18} */
+#define FL_USER19 RBIMPL_CAST((VALUE)(unsigned int)RUBY_FL_USER19) /**< @old{RUBY_FL_USER19} */
+
+#define ELTS_SHARED RUBY_ELTS_SHARED /**< @old{RUBY_ELTS_SHARED} */
+#define RB_OBJ_FREEZE rb_obj_freeze_inline /**< @alias{rb_obj_freeze_inline} */
+
+/** @cond INTERNAL_MACRO */
+#define RUBY_ELTS_SHARED RUBY_ELTS_SHARED
+#define RB_FL_ABLE RB_FL_ABLE
+#define RB_FL_ALL RB_FL_ALL
+#define RB_FL_ALL_RAW RB_FL_ALL_RAW
+#define RB_FL_ANY RB_FL_ANY
+#define RB_FL_ANY_RAW RB_FL_ANY_RAW
+#define RB_FL_REVERSE RB_FL_REVERSE
+#define RB_FL_REVERSE_RAW RB_FL_REVERSE_RAW
+#define RB_FL_SET RB_FL_SET
+#define RB_FL_SET_RAW RB_FL_SET_RAW
+#define RB_FL_TEST RB_FL_TEST
+#define RB_FL_TEST_RAW RB_FL_TEST_RAW
+#define RB_FL_UNSET RB_FL_UNSET
+#define RB_FL_UNSET_RAW RB_FL_UNSET_RAW
+#define RB_OBJ_FREEZE_RAW RB_OBJ_FREEZE_RAW
+#define RB_OBJ_FROZEN RB_OBJ_FROZEN
+#define RB_OBJ_FROZEN_RAW RB_OBJ_FROZEN_RAW
+#define RB_OBJ_UNTRUST RB_OBJ_TAINT
+#define RB_OBJ_UNTRUSTED RB_OBJ_TAINTED
+/** @endcond */
+
+/**
+ * @defgroup deprecated_macros Deprecated macro APIs
+ * @{
+ * These macros are deprecated. Prefer their `RB_`-prefixed versions.
+ */
+#define FL_ABLE RB_FL_ABLE /**< @old{RB_FL_ABLE} */
+#define FL_ALL RB_FL_ALL /**< @old{RB_FL_ALL} */
+#define FL_ALL_RAW RB_FL_ALL_RAW /**< @old{RB_FL_ALL_RAW} */
+#define FL_ANY RB_FL_ANY /**< @old{RB_FL_ANY} */
+#define FL_ANY_RAW RB_FL_ANY_RAW /**< @old{RB_FL_ANY_RAW} */
+#define FL_REVERSE RB_FL_REVERSE /**< @old{RB_FL_REVERSE} */
+#define FL_REVERSE_RAW RB_FL_REVERSE_RAW /**< @old{RB_FL_REVERSE_RAW} */
+#define FL_SET RB_FL_SET /**< @old{RB_FL_SET} */
+#define FL_SET_RAW RB_FL_SET_RAW /**< @old{RB_FL_SET_RAW} */
+#define FL_TEST RB_FL_TEST /**< @old{RB_FL_TEST} */
+#define FL_TEST_RAW RB_FL_TEST_RAW /**< @old{RB_FL_TEST_RAW} */
+#define FL_UNSET RB_FL_UNSET /**< @old{RB_FL_UNSET} */
+#define FL_UNSET_RAW RB_FL_UNSET_RAW /**< @old{RB_FL_UNSET_RAW} */
+#define OBJ_FREEZE RB_OBJ_FREEZE /**< @old{RB_OBJ_FREEZE} */
+#define OBJ_FREEZE_RAW RB_OBJ_FREEZE_RAW /**< @old{RB_OBJ_FREEZE_RAW} */
+#define OBJ_FROZEN RB_OBJ_FROZEN /**< @old{RB_OBJ_FROZEN} */
+#define OBJ_FROZEN_RAW RB_OBJ_FROZEN_RAW /**< @old{RB_OBJ_FROZEN_RAW} */
+#define OBJ_INFECT RB_OBJ_INFECT /**< @old{RB_OBJ_INFECT} */
+#define OBJ_INFECT_RAW RB_OBJ_INFECT_RAW /**< @old{RB_OBJ_INFECT_RAW} */
+#define OBJ_TAINT RB_OBJ_TAINT /**< @old{RB_OBJ_TAINT} */
+#define OBJ_TAINTABLE RB_OBJ_TAINTABLE /**< @old{RB_OBJ_TAINT_RAW} */
+#define OBJ_TAINTED RB_OBJ_TAINTED /**< @old{RB_OBJ_TAINTED} */
+#define OBJ_TAINTED_RAW RB_OBJ_TAINTED_RAW /**< @old{RB_OBJ_TAINTED_RAW} */
+#define OBJ_TAINT_RAW RB_OBJ_TAINT_RAW /**< @old{RB_OBJ_TAINT_RAW} */
+#define OBJ_UNTRUST RB_OBJ_UNTRUST /**< @old{RB_OBJ_TAINT} */
+#define OBJ_UNTRUSTED RB_OBJ_UNTRUSTED /**< @old{RB_OBJ_TAINTED} */
+/** @} */
+
+/**
+ * This is an enum because GDB wants it (rather than a macro). People need not
+ * bother.
+ */
+enum ruby_fl_ushift {
+ /**
+ * Number of bits in ::ruby_fl_type that are _not_ open to users. This is
+ * an implementation detail. Please ignore.
+ */
+ RUBY_FL_USHIFT = 12
+};
+
+/* > The expression that defines the value of an enumeration constant shall be
+ * > an integer constant expression that has a value representable as an `int`.
+ *
+ * -- ISO/IEC 9899:2018 section 6.7.2.2
+ *
+ * So ENUM_OVER_INT situation is an extension to the standard. Note however
+ * that we do not support 16 bit `int` environment. */
+RB_GNUC_EXTENSION
+/**
+ * The flags. Each ruby objects have their own characteristics apart from
+ * their classes. For instance whether an object is frozen or not is not
+ * controlled by its class. This is the type that represents such properties.
+ *
+ * @note About the `FL_USER` terminology: the "user" here does not necessarily
+ * mean only you. For instance struct ::RString instances use these
+ * bits to cache their encodings etc. Devs discussed about this topic,
+ * reached their consensus that ::RUBY_T_DATA is the only valid data
+ * structure that can use these bits; other data structures including
+ * ::RUBY_T_OBJECT use these bits for their own purpose. See also
+ * https://bugs.ruby-lang.org/issues/18059
+ */
+enum
+RBIMPL_ATTR_FLAG_ENUM()
+ruby_fl_type {
+
+ /**
+ * @deprecated This flag once was a thing back in the old days, but makes
+ * no sense any longer today. Exists here for backwards
+ * compatibility only. You can safely forget about it.
+ *
+ * @internal
+ *
+ * The reality is our GC no longer remembers write barriers inside of each
+ * objects, to use dedicated bitmap instead. But this flag is still used
+ * internally. The current usages of this flag should be something
+ * different, which is unclear to @shyouhei.
+ */
+ RUBY_FL_WB_PROTECTED = (1<<5),
+
+ /**
+ * Ruby objects are "generational". There are young objects & old objects.
+ * Young objects are prone to die & monitored relatively extensively by the
+ * garbage collector. Old objects tend to live longer & are monitored less
+ * frequently. When an object survives a GC, its age is incremented. When
+ * age is equal to RVALUE_OLD_AGE, the object becomes Old. This flag is set
+ * when an object becomes old, and is used by the write barrier to check if
+ * an old object should be considered for marking more frequently - as old
+ * objects that have references added between major GCs need to be remarked
+ * to prevent the referred object being mistakenly swept.
+ *
+ * @internal
+ *
+ * But honestly, @shyouhei doesn't think this flag should be visible from
+ * 3rd parties. It must be an implementation detail that they should never
+ * know. Might better be hidden.
+ */
+ RUBY_FL_PROMOTED = (1<<5),
+
+ /**
+ * This flag meaning is type dependent, currently only used by T_DATA.
+ *
+ * @internal
+ */
+ RUBY_FL_USERPRIV0 = (1<<6),
+
+ /**
+ * This flag has something to do with finalisers. A ruby object can have
+ * its finaliser, which is another object that evaluates when the target
+ * object is about to die. This flag is used to denote that there is an
+ * attached finaliser.
+ *
+ * @internal
+ *
+ * But honestly, @shyouhei doesn't think this flag should be visible from
+ * 3rd parties. It must be an implementation detail that they should never
+ * know. Might better be hidden.
+ */
+ RUBY_FL_FINALIZE = (1<<7),
+
+ /**
+ * @deprecated This flag was an implementation detail that should never have
+ * no been exposed. Exists here for backwards
+ * compatibility only. You can safely forget about it.
+ */
+ RUBY_FL_EXIVAR
+
+#if defined(RBIMPL_HAVE_ENUM_ATTRIBUTE)
+ RBIMPL_ATTR_DEPRECATED(("FL_EXIVAR is an outdated implementation detail, it should not be used."))
+#elif defined(_MSC_VER)
+# pragma deprecated(RUBY_FL_EXIVAR)
+#endif
+
+ = 0,
+
+ /**
+ * This flag has something to do with Ractor. Multiple Ractors run without
+ * protecting each other. Sharing an object among Ractors are basically
+ * dangerous, disabled by default. This flag is used to bypass that
+ * restriction. Of course, you have to manually prevent race conditions
+ * then.
+ *
+ * This flag needs deep understanding of multithreaded programming. You
+ * would better not use it.
+ */
+ RUBY_FL_SHAREABLE = (1<<8),
+
+ /**
+ * This object weakly refers to other objects.
+ *
+ * @internal
+ */
+ RUBY_FL_WEAK_REFERENCE = (1<<9),
+
+ /**
+ * This flag is no longer in use
+ *
+ * @internal
+ */
+ RUBY_FL_UNUSED10 = (1<<10),
+
+ /**
+ * This flag has something to do with data immutability. When this flag is
+ * set an object is considered "frozen". No modification are expected to
+ * happen beyond that point for the particular object. Immutability is
+ * basically considered to be a good property these days. Library authors
+ * are expected to obey. Test this bit before you touch a data structure.
+ *
+ * @see rb_check_frozen()
+ */
+ RUBY_FL_FREEZE = (1<<11),
+
+/** (@shyouhei doesn't know how to excude this macro from doxygen). */
+#define RBIMPL_FL_USER_N(n) RUBY_FL_USER##n = (1<<(RUBY_FL_USHIFT+n))
+ RBIMPL_FL_USER_N(0), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(1), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(2), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(3), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(4), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(5), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(6), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(7), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(8), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(9), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(10), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(11), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(12), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(13), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(14), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(15), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(16), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(17), /**< User-defined flag. */
+ RBIMPL_FL_USER_N(18), /**< User-defined flag. */
+#ifdef ENUM_OVER_INT
+ RBIMPL_FL_USER_N(19), /**< User-defined flag. */
+#else
+# define RUBY_FL_USER19 (RBIMPL_VALUE_ONE<<(RUBY_FL_USHIFT+19))
+#endif
+#undef RBIMPL_FL_USER_N
+#undef RBIMPL_WIDER_ENUM
+
+ /**
+ * This flag has something to do with data structures. Over time, ruby
+ * evolved to reduce memory footprints. One of such attempt is so-called
+ * copy-on-write, which delays duplication of resources until ultimately
+ * necessary. Some data structures share this scheme. For example
+ * multiple instances of struct ::RArray could point identical memory
+ * region in common, as long as they don't differ. As people favour
+ * immutable style of programming than before, this situation is getting
+ * more and more common. Because such "shared" memory regions have nuanced
+ * ownership by nature, each structures need special care for them. This
+ * flag is used to distinguish such shared constructs.
+ *
+ * @internal
+ *
+ * But honestly, @shyouhei doesn't think this flag should be visible from
+ * 3rd parties. It must be an implementation detail that they should never
+ * know. Might better be hidden.
+ */
+ RUBY_ELTS_SHARED = RUBY_FL_USER0,
+
+ /**
+ * This flag has something to do with an object's class. There are kind of
+ * classes called "singleton class", each of which have exactly one
+ * instance. What is interesting about singleton classes is that they are
+ * created _after_ their instance were instantiated, like this:
+ *
+ * ```ruby
+ * foo = Object.new # foo is an instance of Object...
+ * bar = foo.singleton_class # foo is now an instance of bar.
+ * ```
+ *
+ * Here as you see `bar` is a singleton class of `foo`, which is injected
+ * into `foo`'s inheritance tree in a different statement (== distinct
+ * sequence point). In order to achieve this property singleton classes
+ * are special-cased in the interpreter. There is one bit flag that
+ * distinguishes if a class is a singleton class or not, and this is it.
+ *
+ * @internal
+ *
+ * But honestly, @shyouhei doesn't think this flag should be visible from
+ * 3rd parties. It must be an implementation detail that they should never
+ * know. Might better be hidden.
+ */
+ RUBY_FL_SINGLETON = RUBY_FL_USER1,
+};
+
+#undef RBIMPL_HAVE_ENUM_ATTRIBUTE
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * This is an implementation detail of #RB_OBJ_FREEZE(). People don't use it
+ * directly.
+ *
+ * @param[out] klass A singleton class.
+ * @post `klass` gets frozen.
+ */
+void rb_freeze_singleton_class(VALUE klass);
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_FORCEINLINE()
+/**
+ * Checks if the object is flaggable. There are some special cases (most
+ * notably ::RUBY_Qfalse) where appending a flag to an object is not possible.
+ * This function can detect that.
+ *
+ * @param[in] obj Object in question
+ * @retval true It is flaggable.
+ * @retval false No it isn't.
+ */
+static bool
+RB_FL_ABLE(VALUE obj)
+{
+ if (RB_SPECIAL_CONST_P(obj)) {
+ return false;
+ }
+ else {
+ RBIMPL_ASSERT_OR_ASSUME(!RB_TYPE_P(obj, RUBY_T_NODE));
+ return true;
+ }
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is an implementation detail of RB_FL_TEST(). 3rd parties need not use
+ * this. Just always use RB_FL_TEST().
+ *
+ * @param[in] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @pre The object must not be an enum ::ruby_special_consts.
+ * @return `obj`'s flags, masked by `flags`.
+ */
+static inline VALUE
+RB_FL_TEST_RAW(VALUE obj, VALUE flags)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FL_ABLE(obj));
+ return RBASIC(obj)->flags & flags;
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Tests if the given flag(s) are set or not. You can pass multiple flags at
+ * once:
+ *
+ * ```CXX
+ * auto obj = rb_eval_string("...");
+ * if (RB_FL_TEST(obj, RUBY_FL_FREEZE | RUBY_FL_SHAREABLE)) {
+ * printf("Ractor ready!\n");
+ * }
+ * ```
+ *
+ * @param[in] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @return `obj`'s flags, masked by `flags`.
+ * @note It is intentional for this function to return ::VALUE. The
+ * return value could be passed to RB_FL_STE() etc.
+ */
+static inline VALUE
+RB_FL_TEST(VALUE obj, VALUE flags)
+{
+ if (RB_FL_ABLE(obj)) {
+ return RB_FL_TEST_RAW(obj, flags);
+ }
+ else {
+ return RBIMPL_VALUE_NULL;
+ }
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is an implementation detail of RB_FL_ANY(). 3rd parties need not use
+ * this. Just always use RB_FL_ANY().
+ *
+ * @param[in] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @retval true The object has any of the flags set.
+ * @retval false No it doesn't at all.
+ * @pre The object must not be an enum ::ruby_special_consts.
+ */
+static inline bool
+RB_FL_ANY_RAW(VALUE obj, VALUE flags)
+{
+ return RB_FL_TEST_RAW(obj, flags);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Identical to RB_FL_TEST(), except it returns bool.
+ *
+ * @param[in] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @retval true The object has any of the flags set.
+ * @retval false No it doesn't at all.
+ */
+static inline bool
+RB_FL_ANY(VALUE obj, VALUE flags)
+{
+ return RB_FL_TEST(obj, flags);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is an implementation detail of RB_FL_ALL(). 3rd parties need not use
+ * this. Just always use RB_FL_ALL().
+ *
+ * @param[in] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @retval true The object has all of the flags set.
+ * @retval false The object lacks any of the flags.
+ * @pre The object must not be an enum ::ruby_special_consts.
+ */
+static inline bool
+RB_FL_ALL_RAW(VALUE obj, VALUE flags)
+{
+ return RB_FL_TEST_RAW(obj, flags) == flags;
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Identical to RB_FL_ANY(), except it mandates all passed flags be set.
+ *
+ * @param[in] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @retval true The object has all of the flags set.
+ * @retval false The object lacks any of the flags.
+ */
+static inline bool
+RB_FL_ALL(VALUE obj, VALUE flags)
+{
+ return RB_FL_TEST(obj, flags) == flags;
+}
+
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * @private
+ *
+ * This is an implementation detail of RB_FL_SET(). 3rd parties need not use
+ * this. Just always use RB_FL_SET().
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` set.
+ *
+ * @internal
+ *
+ * This is function is here to annotate a part of RB_FL_SET_RAW() as
+ * `__declspec(noalias)`.
+ */
+static inline void
+rbimpl_fl_set_raw_raw(struct RBasic *obj, VALUE flags)
+{
+ obj->flags |= flags;
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is an implementation detail of RB_FL_SET(). 3rd parties need not use
+ * this. Just always use RB_FL_SET().
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` set.
+ */
+static inline void
+RB_FL_SET_RAW(VALUE obj, VALUE flags)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FL_ABLE(obj));
+ rbimpl_fl_set_raw_raw(RBASIC(obj), flags);
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Sets the given flag(s).
+ *
+ * ```CXX
+ * auto v = rb_eval_string("...");
+ * RB_FL_SET(v, RUBY_FL_FREEZE);
+ * ```
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` set.
+ */
+static inline void
+RB_FL_SET(VALUE obj, VALUE flags)
+{
+ if (RB_FL_ABLE(obj)) {
+ RB_FL_SET_RAW(obj, flags);
+ }
+}
+
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * @private
+ *
+ * This is an implementation detail of RB_FL_UNSET(). 3rd parties need not use
+ * this. Just always use RB_FL_UNSET().
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` cleared.
+ *
+ * @internal
+ *
+ * This is function is here to annotate a part of RB_FL_UNSET_RAW() as
+ * `__declspec(noalias)`.
+ */
+static inline void
+rbimpl_fl_unset_raw_raw(struct RBasic *obj, VALUE flags)
+{
+ obj->flags &= ~flags;
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is an implementation detail of RB_FL_UNSET(). 3rd parties need not use
+ * this. Just always use RB_FL_UNSET().
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` cleared.
+ */
+static inline void
+RB_FL_UNSET_RAW(VALUE obj, VALUE flags)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FL_ABLE(obj));
+ rbimpl_fl_unset_raw_raw(RBASIC(obj), flags);
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Clears the given flag(s).
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` cleard.
+ */
+static inline void
+RB_FL_UNSET(VALUE obj, VALUE flags)
+{
+ if (RB_FL_ABLE(obj)) {
+ RB_FL_UNSET_RAW(obj, flags);
+ }
+}
+
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * @private
+ *
+ * This is an implementation detail of RB_FL_REVERSE(). 3rd parties need not
+ * use this. Just always use RB_FL_REVERSE().
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` reversed.
+ *
+ * @internal
+ *
+ * This is function is here to annotate a part of RB_FL_REVERSE_RAW() as
+ * `__declspec(noalias)`.
+ */
+static inline void
+rbimpl_fl_reverse_raw_raw(struct RBasic *obj, VALUE flags)
+{
+ obj->flags ^= flags;
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is an implementation detail of RB_FL_REVERSE(). 3rd parties need not
+ * use this. Just always use RB_FL_REVERSE().
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` cleared.
+ */
+static inline void
+RB_FL_REVERSE_RAW(VALUE obj, VALUE flags)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FL_ABLE(obj));
+ rbimpl_fl_reverse_raw_raw(RBASIC(obj), flags);
+}
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Reverses the flags. This function is here mainly for symmetry on set/unset.
+ * Rarely used in practice.
+ *
+ * @param[out] obj Object in question.
+ * @param[in] flags A set of enum ::ruby_fl_type.
+ * @post `obj` has `flags` reversed.
+ */
+static inline void
+RB_FL_REVERSE(VALUE obj, VALUE flags)
+{
+ if (RB_FL_ABLE(obj)) {
+ RB_FL_REVERSE_RAW(obj, flags);
+ }
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is an implementation detail of RB_OBJ_FROZEN(). 3rd parties need not
+ * use this. Just always use RB_OBJ_FROZEN().
+ *
+ * @param[in] obj Object in question.
+ * @retval RUBY_FL_FREEZE Yes it is.
+ * @retval 0 No it isn't.
+ *
+ * @internal
+ *
+ * It is intentional not to return bool here. There is a place in ruby core
+ * (namely `class.c:singleton_class_of()`) where return value of this function
+ * is passed to RB_FL_SET_RAW().
+ */
+static inline VALUE
+RB_OBJ_FROZEN_RAW(VALUE obj)
+{
+ return RB_FL_TEST_RAW(obj, RUBY_FL_FREEZE);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if an object is frozen.
+ *
+ * @param[in] obj Object in question.
+ * @retval true Yes it is.
+ * @retval false No it isn't.
+ */
+static inline bool
+RB_OBJ_FROZEN(VALUE obj)
+{
+ if (! RB_FL_ABLE(obj)) {
+ return true;
+ }
+ else {
+ return RB_OBJ_FROZEN_RAW(obj);
+ }
+}
+
+RUBY_SYMBOL_EXPORT_BEGIN
+/**
+ * Prevents further modifications to the given object. ::rb_eFrozenError shall
+ * be raised if modification is attempted.
+ *
+ * @param[out] x Object in question.
+ * @exception rb_eNoMemError Failed to allocate memory for the frozen
+ * representation of the object.
+ */
+void rb_obj_freeze_inline(VALUE obj);
+RUBY_SYMBOL_EXPORT_END
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is an implementation detail of RB_OBJ_FREEZE(). 3rd parties need not
+ * use this. Just always use RB_OBJ_FREEZE().
+ *
+ * @param[out] obj Object in question.
+ */
+static inline void
+RB_OBJ_FREEZE_RAW(VALUE obj)
+{
+ rb_obj_freeze_inline(obj);
+}
+
+#endif /* RBIMPL_FL_TYPE_H */
diff --git a/include/ruby/internal/gc.h b/include/ruby/internal/gc.h
new file mode 100644
index 0000000000..21c2b670b3
--- /dev/null
+++ b/include/ruby/internal/gc.h
@@ -0,0 +1,826 @@
+#ifndef RBIMPL_GC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_GC_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Registering values to the GC.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h> /* size_t */
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h> /* ssize_t */
+#endif
+
+#include "ruby/assert.h"
+#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/maybe_unused.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/internal/stdbool.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+#define RUBY_REF_EDGE(s, p) offsetof(s, p)
+#define RUBY_REFS_LIST_PTR(l) (RUBY_DATA_FUNC)(uintptr_t)(l)
+#define RUBY_REF_END SIZE_MAX
+#define RUBY_REFERENCES(t) static const size_t t[]
+#define RUBY_REFERENCES_START(t) RUBY_REFERENCES(t) = {
+#define RUBY_REFERENCES_END RUBY_REF_END, };
+
+/* gc.c */
+
+RBIMPL_ATTR_COLD()
+RBIMPL_ATTR_NORETURN()
+/**
+ * Triggers out-of-memory error. If possible it raises ::rb_eNoMemError. But
+ * because we are running out of memory that is not always doable. This
+ * function tries hard to show something, but ultimately can die silently.
+ *
+ * @exception rb_eNoMemError Raises it if possible.
+ */
+void rb_memerror(void);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries if the GC is busy.
+ *
+ * @retval 0 It isn't.
+ * @retval 1 It is.
+ */
+int rb_during_gc(void);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Marks objects between the two pointers. This is one of the GC utility
+ * functions that you can call when you design your own
+ * ::rb_data_type_struct::dmark.
+ *
+ * @pre Continuous memory region from `start` to `end` shall be fully
+ * addressable.
+ * @param[out] start Pointer to an array of objects.
+ * @param[out] end Pointer that terminates the array of objects.
+ * @post Objects from `start` (included) to `end` (excluded) are marked.
+ *
+ * @internal
+ *
+ * `end` can be NULL... But that just results in no-op.
+ */
+void rb_gc_mark_locations(const VALUE *start, const VALUE *end);
+
+/**
+ * Identical to rb_mark_hash(), except it marks only values of the table and
+ * leave their associated keys unmarked. This is one of the GC utility
+ * functions that you can call when you design your own
+ * ::rb_data_type_struct::dmark.
+ *
+ * @warning Of course it can break GC. Leave it unused if unsure.
+ * @param[in] tbl A table to mark.
+ * @post Values stored in `tbl` are marked.
+ */
+void rb_mark_tbl(struct st_table *tbl);
+
+/**
+ * Identical to rb_mark_tbl(), except it marks objects using
+ * rb_gc_mark_movable(). This is one of the GC utility functions that you can
+ * call when you design your own ::rb_data_type_struct::dmark.
+ *
+ * @warning Of course it can break GC. Leave it unused if unsure.
+ * @param[in] tbl A table to mark.
+ * @post Values stored in `tbl` are marked.
+ */
+void rb_mark_tbl_no_pin(struct st_table *tbl);
+
+/**
+ * Identical to rb_mark_hash(), except it marks only keys of the table and
+ * leave their associated values unmarked. This is one of the GC utility
+ * functions that you can call when you design your own
+ * ::rb_data_type_struct::dmark.
+ *
+ * @warning Of course it can break GC. Leave it unused if unsure.
+ * @param[in] tbl A table to mark.
+ * @post Keys stored in `tbl` are marked.
+ */
+void rb_mark_set(struct st_table *tbl);
+
+/**
+ * Marks keys and values associated inside of the given table. This is one of
+ * the GC utility functions that you can call when you design your own
+ * ::rb_data_type_struct::dmark.
+ *
+ * @param[in] tbl A table to mark.
+ * @post Objects stored in `tbl` are marked.
+ */
+void rb_mark_hash(struct st_table *tbl);
+
+/**
+ * Updates references inside of tables. After you marked values using
+ * rb_mark_tbl_no_pin(), the objects inside of the table could of course be
+ * moved. This function is to fixup those references. You can call this from
+ * your ::rb_data_type_struct::dcompact.
+ *
+ * @param[out] ptr A table that potentially includes moved references.
+ * @post Moved references, if any, are corrected.
+ */
+void rb_gc_update_tbl_refs(st_table *ptr);
+
+/**
+ * Identical to rb_gc_mark(), except it allows the passed value be a
+ * non-object. For instance pointers to different type of memory regions are
+ * allowed here. Such values are silently ignored. This is one of the GC
+ * utility functions that you can call when you design your own
+ * ::rb_data_type_struct::dmark.
+ *
+ * @param[out] obj A possible object.
+ * @post `obj` is marked, if possible.
+ */
+void rb_gc_mark_maybe(VALUE obj);
+
+/**
+ * Marks an object. This is one of the GC utility functions that you can call
+ * when you design your own ::rb_data_type_struct::dmark.
+ *
+ * @param[out] obj Arbitrary Ruby object.
+ * @post `obj` is marked.
+ */
+void rb_gc_mark(VALUE obj);
+
+/**
+ * Maybe this is the only function provided for C extensions to control the
+ * pinning of objects, so let us describe it in detail. These days Ruby's GC
+ * is copying. As far as an object's physical address is guaranteed unused, it
+ * can move around the object space. Our GC engine rearranges these objects
+ * after it reclaims unreachable objects from our object space, so that the
+ * space is compact (improves memory locality). This is called the
+ * "compaction" phase, and works well most of the time... as far as there are
+ * no C extensions. C extensions complicate the scenario because Ruby core
+ * cannot detect any use of the physical address of an object inside of C
+ * functions. In order to prevent memory corruptions, objects observable from
+ * C extensions are "pinned"; they stick to where they are born until they die,
+ * just in case any C extensions touch their raw pointers. This variant of
+ * scheme is called "Mostly-Copying" garbage collector. Authors of C
+ * extensions, however, can extremely carefully write them to become
+ * compaction-aware. To do so avoid referring to a Ruby object from inside of
+ * your struct in the first place. But if that is not possible, use this
+ * function from your ::rb_data_type_struct::dmark then. This way objects
+ * marked using it are considered movable. If you chose this way you have to
+ * manually fix up locations of such moved pointers using rb_gc_location().
+ *
+ * @see Bartlett, Joel F., "Compacting Garbage Collection with Ambiguous
+ * Roots", ACM SIGPLAN Lisp Pointers Volume 1 Issue 6 pp. 3-12,
+ * April-May-June, 1988. https://doi.org/10.1145/1317224.1317225
+ *
+ * @param[in] obj Object that is movable.
+ * @post Values stored in `tbl` are marked.
+ */
+void rb_gc_mark_movable(VALUE obj);
+
+/**
+ * Finds a new "location" of an object. An object can be moved on compaction.
+ * This function projects its new abode, or just returns the passed object if
+ * not moved. This is one of the GC utility functions that you can call when
+ * you design your own ::rb_data_type_struct::dcompact.
+ *
+ * @param[in] obj An object, possibly already moved to somewhere else.
+ * @return An object, which holds the current contents of former `obj`.
+ */
+VALUE rb_gc_location(VALUE obj);
+
+/**
+ * Triggers a GC process. This was the only GC entry point that we had at the
+ * beginning. Over time our GC evolved. Now what this function does is just a
+ * very simplified variation of the entire GC algorithms. A series of
+ * procedures kicked by this API is called a "full" GC.
+ *
+ * - It immediately scans the entire object space to sort the dead.
+ * - It immediately reclaims any single dead bodies to reuse later.
+ *
+ * It is worth noting that the procedures above do not include evaluations of
+ * finalisers. They run later.
+ *
+ * @internal
+ *
+ * Finalisers are deferred until we can handle interrupts. See
+ * `rb_postponed_job_flush` in vm_trace.c.
+ *
+ * Of course there are GC that are not "full". For instance this one and the
+ * GC which runs when we are running out of memory are different. See
+ * `gc_profile_record_flag` defined in gc.c for the kinds of GC.
+ *
+ * In spite of the name this is not what everything that a GC can trigger. As
+ * of writing it seems this function does not trigger compaction. But this
+ * might change in future.
+ */
+void rb_gc(void);
+
+/**
+ * Copy&paste an object's finaliser to another. This is one of the GC utility
+ * functions that you can call when you design your own `initialize_copy`,
+ * `initialize_dup`, `initialize_clone`.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Source object.
+ * @post `dst` and `src` share the same finaliser.
+ *
+ * @internal
+ *
+ * But isn't it easier for you to call super, and let `Object#initialize_copy`
+ * call this function instead?
+ */
+void rb_gc_copy_finalizer(VALUE dst, VALUE src);
+
+/**
+ * (Re-) enables GC. This makes sense only after you called rb_gc_disable().
+ *
+ * @retval RUBY_Qtrue GC was disabled before.
+ * @retval RUBY_Qfalse GC was enabled before.
+ * @post GC is enabled.
+ *
+ * @internal
+ *
+ * This is one of such exceptional functions that does not raise both Ruby
+ * exceptions and C++ exceptions.
+ */
+VALUE rb_gc_enable(void);
+
+/**
+ * Disables GC. This prevents automatic GC runs when the process is running
+ * out of memory. Such situations shall result in rb_memerror(). However this
+ * does not prevent users from manually invoking rb_gc(). That should work.
+ * People abused this by disabling GC at the beginning of an event loop,
+ * process events without GC overheads, then manually force reclaiming garbage
+ * at the bottom of the loop. However because our GC is now much smarter than
+ * just calling rb_gc(), this technique is proven to be sub-optimal these days.
+ * It is believed that there is currently practically no needs of this
+ * function.
+ *
+ * @retval RUBY_Qtrue GC was disabled before.
+ * @retval RUBY_Qfalse GC was enabled before.
+ * @post GC is disabled.
+ */
+VALUE rb_gc_disable(void);
+
+/**
+ * Identical to rb_gc(), except the return value.
+ *
+ * @return Always returns ::RUBY_Qnil.
+ */
+VALUE rb_gc_start(void);
+
+/**
+ * Assigns a finaliser for an object. Each objects can have objects (typically
+ * blocks) that run immediately after that object dies. They are called
+ * finalisers of an object. This function associates a finaliser object with a
+ * target object.
+ *
+ * @note Note that finalisers run _after_ the object they finalise dies. You
+ * cannot for instance call its methods.
+ * @note If your finaliser references the object it finalises that object
+ * loses any chance to become a garbage; effectively leaks memory until
+ * the end of the process.
+ *
+ * @param[in] obj Target to finalise.
+ * @param[in] block Something `call`able.
+ * @exception rb_eRuntimeError Somehow `obj` cannot have finalisers.
+ * @exception rb_eFrozenError `obj` is frozen.
+ * @exception rb_eArgError `block` doesn't respond to `call`.
+ * @return The passed `block`.
+ * @post `block` runs after `obj` dies.
+ */
+VALUE rb_define_finalizer(VALUE obj, VALUE block);
+
+/**
+ * Modifies the object so that it has no finalisers at all. This function is
+ * mainly provided for symmetry. No practical usages can be thought of.
+ *
+ * @param[out] obj Object to clear its finalisers.
+ * @exception rb_eFrozenError `obj` is frozen.
+ * @return The passed `obj`.
+ * @post `obj` has no finalisers.
+ * @note There is no way to undefine a specific part of many finalisers
+ * that `obj` could have. All you can do is to clear them all.
+ */
+VALUE rb_undefine_finalizer(VALUE obj);
+
+/**
+ * Identical to rb_gc_stat(), with "count" parameter.
+ *
+ * @return Lifetime total number of runs of GC.
+ */
+size_t rb_gc_count(void);
+
+/**
+ * Obtains various GC related profiles. The parameter can be either a Symbol
+ * or a Hash. If a Hash is passed, it is filled with everything currently
+ * available. If a Symbol is passed just that portion is returned.
+ *
+ * Possible variations of keys you can pass here change from version to
+ * version. You can get the list of known keys by passing an empty hash and
+ * let it be filled.
+ *
+ * @param[in,out] key_or_buf A Symbol, or a Hash.
+ * @exception rb_eTypeError Neither Symbol nor Hash.
+ * @exception rb_eFrozenError Frozen hash is passed.
+ * @return In case a Hash is passed it returns 0. Otherwise the
+ * profile value associated with the given key is returned.
+ * @post In case a Hash is passed it is filled with values.
+ */
+size_t rb_gc_stat(VALUE key_or_buf);
+
+/**
+ * Obtains various info regarding the most recent GC run. This includes for
+ * instance the reason of the GC. The parameter can be either a Symbol or a
+ * Hash. If a Hash is passed, it is filled with everything currently
+ * available. If a Symbol is passed just that portion is returned.
+ *
+ * Possible variations of keys you can pass here change from version to
+ * version. You can get the list of known keys by passing an empty hash and
+ * let it be filled.
+ *
+ * @param[in,out] key_or_buf A Symbol, or a Hash.
+ * @exception rb_eTypeError Neither Symbol nor Hash.
+ * @exception rb_eFrozenError Frozen hash is passed.
+ * @return In case a Hash is passed it returns that hash. Otherwise
+ * the profile value associated with the given key is returned.
+ * @post In case a Hash is passed it is filled with values.
+ */
+VALUE rb_gc_latest_gc_info(VALUE key_or_buf);
+
+/**
+ * Informs that there are external memory usages. Our GC runs when we are
+ * running out of memory. The amount of memory, however, can increase/decrease
+ * behind-the-scene. For instance DLLs can allocate memories using `mmap(2)`
+ * etc, which are opaque to us. Registering such external allocations using
+ * this function enables proper detection of how much memories an object used
+ * as a whole. That will trigger GCs more often than it would otherwise. You
+ * can also pass negative numbers here, to indicate that such external
+ * allocations are gone.
+ *
+ * @param[in] diff Amount of memory increased(+)/decreased(-).
+ */
+void rb_gc_adjust_memory_usage(ssize_t diff);
+
+/**
+ * Inform the garbage collector that the global or static variable pointed by
+ * `valptr` stores a live Ruby object that should not be moved. Note that
+ * extensions should use this API on global constants instead of assuming
+ * constants defined in Ruby are always alive. Ruby code can remove global
+ * constants.
+ *
+ * Because this registration itself has a possibility to trigger a GC, this
+ * function must be called before any GC-able objects is assigned to the
+ * address pointed by `valptr`.
+ */
+void rb_gc_register_address(VALUE *valptr);
+
+/**
+ * An alias for `rb_gc_register_address()`.
+ */
+void rb_global_variable(VALUE *);
+
+/**
+ * Inform the garbage collector that a pointer previously passed to
+ * `rb_gc_register_address()` no longer points to a live Ruby object.
+ */
+void rb_gc_unregister_address(VALUE *valptr);
+
+/**
+ * Inform the garbage collector that `object` is a live Ruby object that should
+ * not be moved.
+ *
+ * See also: rb_gc_register_address()
+ */
+void rb_gc_register_mark_object(VALUE object);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#undef USE_RGENGC
+#define USE_RGENGC 1
+
+/**
+ * @deprecated This macro seems broken. Setting this to anything other than
+ * zero just doesn't compile. We need to KonMari.
+ */
+#ifndef USE_RGENGC_LOGGING_WB_UNPROTECT
+# define USE_RGENGC_LOGGING_WB_UNPROTECT 0
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RArray. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_ARRAY
+# define RGENGC_WB_PROTECTED_ARRAY 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RHash. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_HASH
+# define RGENGC_WB_PROTECTED_HASH 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RStruct. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_STRUCT
+# define RGENGC_WB_PROTECTED_STRUCT 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RString. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_STRING
+# define RGENGC_WB_PROTECTED_STRING 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RObject. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_OBJECT
+# define RGENGC_WB_PROTECTED_OBJECT 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RRegexp. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_REGEXP
+# define RGENGC_WB_PROTECTED_REGEXP 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RMatch. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_MATCH
+# define RGENGC_WB_PROTECTED_MATCH 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RClass. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_CLASS
+# define RGENGC_WB_PROTECTED_CLASS 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RFloat. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_FLOAT
+# define RGENGC_WB_PROTECTED_FLOAT 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RComplex. It has to be set at the time ruby itself compiles.
+ * Makes no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_COMPLEX
+# define RGENGC_WB_PROTECTED_COMPLEX 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RRational. It has to be set at the time ruby itself compiles.
+ * Makes no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_RATIONAL
+# define RGENGC_WB_PROTECTED_RATIONAL 1
+#endif
+
+/**
+ * @private
+ *
+ * This is a compile-time flag to enable/disable write barrier for
+ * struct ::RBignum. It has to be set at the time ruby itself compiles. Makes
+ * no sense for 3rd parties.
+ */
+#ifndef RGENGC_WB_PROTECTED_BIGNUM
+# define RGENGC_WB_PROTECTED_BIGNUM 1
+#endif
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't think anybody uses this right now.
+ */
+#ifndef RGENGC_WB_PROTECTED_NODE_CREF
+# define RGENGC_WB_PROTECTED_NODE_CREF 1
+#endif
+
+/**
+ * @defgroup rgengc Write barrier (WB) interfaces:
+ *
+ * @note The following core interfaces can be changed in the future. Please
+ * catch up if you want to insert WB into C-extensions correctly.
+ *
+ * @{
+ */
+
+/**
+ * Declaration of a "back" pointer. This is a write barrier for new reference
+ * from "old" generation to "young" generation. It writes `young` into
+ * `*slot`, which is a pointer inside of `old`.
+ *
+ * @param[in] old An old object.
+ * @param[in] slot A pointer inside of `old`.
+ * @param[out] young A young object.
+ */
+#define RB_OBJ_WRITE(old, slot, young) \
+ RBIMPL_CAST(rb_obj_write((VALUE)(old), (VALUE *)(slot), (VALUE)(young), __FILE__, __LINE__))
+
+/**
+ * Identical to #RB_OBJ_WRITE(), except it doesn't write any values, but only a
+ * WB declaration. `oldv` is replaced value with `b` (not used in current
+ * Ruby).
+ *
+ * @param[in] old An old object.
+ * @param[in] oldv An object previously stored inside of `old`.
+ * @param[out] young A young object.
+ */
+#define RB_OBJ_WRITTEN(old, oldv, young) \
+ RBIMPL_CAST(rb_obj_written((VALUE)(old), (VALUE)(oldv), (VALUE)(young), __FILE__, __LINE__))
+/** @} */
+
+#define OBJ_PROMOTED_RAW RB_OBJ_PROMOTED_RAW /**< @old{RB_OBJ_PROMOTED_RAW} */
+#define OBJ_PROMOTED RB_OBJ_PROMOTED /**< @old{RB_OBJ_PROMOTED} */
+#define OBJ_WB_UNPROTECT RB_OBJ_WB_UNPROTECT /**< @old{RB_OBJ_WB_UNPROTECT} */
+
+/**
+ * Asserts that the passed object is not fenced by write barriers. Objects of
+ * such property do not contribute to generational GCs. They are scanned
+ * always.
+ *
+ * @param[out] x An object that would not be protected by the barrier.
+ */
+#define RB_OBJ_WB_UNPROTECT(x) rb_obj_wb_unprotect(x, __FILE__, __LINE__)
+
+/**
+ * Identical to #RB_OBJ_WB_UNPROTECT(), except it can also assert that the
+ * given object is of given type.
+ *
+ * @param[in] type One of `ARRAY`, `STRING`, etc.
+ * @param[out] obj An object of `type` that would not be protected.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't understand why this has to be visible from extensions.
+ */
+#define RB_OBJ_WB_UNPROTECT_FOR(type, obj) \
+ (RGENGC_WB_PROTECTED_##type ? OBJ_WB_UNPROTECT(obj) : obj)
+
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_obj_wb_unprotect(). People don't use
+ * it directly.
+ */
+#define RGENGC_LOGGING_WB_UNPROTECT rb_gc_unprotect_logging
+
+/** @cond INTERNAL_MACRO */
+#define RB_OBJ_PROMOTED_RAW RB_OBJ_PROMOTED_RAW
+#define RB_OBJ_PROMOTED RB_OBJ_PROMOTED
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * This is the implementation of #RB_OBJ_WRITE(). People don't use it
+ * directly.
+ *
+ * @param[in] old An object that points to `young`.
+ * @param[out] young An object that is referenced from `old`.
+ */
+void rb_gc_writebarrier(VALUE old, VALUE young);
+
+/**
+ * This is the implementation of #RB_OBJ_WB_UNPROTECT(). People don't use it
+ * directly.
+ *
+ * @param[out] obj An object that does not participate in WB.
+ */
+void rb_gc_writebarrier_unprotect(VALUE obj);
+
+#if USE_RGENGC_LOGGING_WB_UNPROTECT
+/**
+ * @private
+ *
+ * This is the implementation of #RGENGC_LOGGING_WB_UNPROTECT(). People
+ * don't use it directly.
+ *
+ * @param[in] objptr Don't know why this is a pointer to void but in
+ * reality this is a pointer to an object that is about
+ * to be un-protected.
+ * @param[in] filename Pass C's `__FILE__` here.
+ * @param[in] line Pass C's `__LINE__` here.
+ */
+void rb_gc_unprotect_logging(void *objptr, const char *filename, int line);
+#endif
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * This is the implementation of #RB_OBJ_PROMOTED(). People don't use it
+ * directly.
+ *
+ * @param[in] obj An object to query.
+ * @retval true The object is "promoted".
+ * @retval false The object is young. Have not experienced GC at all.
+ */
+static inline bool
+RB_OBJ_PROMOTED_RAW(VALUE obj)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_FL_ABLE(obj));
+ return RB_FL_ANY_RAW(obj, RUBY_FL_PROMOTED);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Tests if the object is "promoted" -- that is, whether the object experienced
+ * one or more GC marks.
+ *
+ * @param[in] obj An object to query.
+ * @retval true The object is "promoted".
+ * @retval false The object is young. Have not experienced GC at all.
+ * @note Hello, is anyone actively calling this function? @shyouhei have
+ * never seen any actual usages outside of the GC implementation
+ * itself.
+ */
+static inline bool
+RB_OBJ_PROMOTED(VALUE obj)
+{
+ if (! RB_FL_ABLE(obj)) {
+ return false;
+ }
+ else {
+ return RB_OBJ_PROMOTED_RAW(obj);
+ }
+}
+
+/**
+ * This is the implementation of #RB_OBJ_WB_UNPROTECT(). People don't use it
+ * directly.
+ *
+ * @param[out] x An object that does not participate in WB.
+ * @param[in] filename C's `__FILE__` of the caller function.
+ * @param[in] line C's `__LINE__` of the caller function.
+ * @return x
+ */
+static inline VALUE
+rb_obj_wb_unprotect(
+ VALUE x,
+ RBIMPL_ATTR_MAYBE_UNUSED()
+ const char *filename,
+ RBIMPL_ATTR_MAYBE_UNUSED()
+ int line)
+{
+#if USE_RGENGC_LOGGING_WB_UNPROTECT
+ RGENGC_LOGGING_WB_UNPROTECT(RBIMPL_CAST((void *)x), filename, line);
+#endif
+ rb_gc_writebarrier_unprotect(x);
+ return x;
+}
+
+/**
+ * @private
+ *
+ * This is the implementation of #RB_OBJ_WRITTEN(). People don't use it
+ * directly.
+ *
+ * @param[in] a An old object.
+ * @param[in] oldv An object previously stored inside of `old`.
+ * @param[out] b A young object.
+ * @param[in] filename C's `__FILE__` of the caller function.
+ * @param[in] line C's `__LINE__` of the caller function.
+ * @return a
+ */
+static inline VALUE
+rb_obj_written(
+ VALUE a,
+ RBIMPL_ATTR_MAYBE_UNUSED()
+ VALUE oldv,
+ VALUE b,
+ RBIMPL_ATTR_MAYBE_UNUSED()
+ const char *filename,
+ RBIMPL_ATTR_MAYBE_UNUSED()
+ int line)
+{
+#if USE_RGENGC_LOGGING_WB_UNPROTECT
+ RGENGC_LOGGING_OBJ_WRITTEN(a, oldv, b, filename, line);
+#endif
+
+ if (!RB_SPECIAL_CONST_P(b)) {
+ rb_gc_writebarrier(a, b);
+ }
+
+ return a;
+}
+
+/**
+ * @private
+ *
+ * This is the implementation of #RB_OBJ_WRITE(). People don't use it
+ * directly.
+ *
+ * @param[in] a An old object.
+ * @param[in] slot A pointer inside of `old`.
+ * @param[out] b A young object.
+ * @param[in] filename C's `__FILE__` of the caller function.
+ * @param[in] line C's `__LINE__` of the caller function.
+ * @return a
+ */
+static inline VALUE
+rb_obj_write(
+ VALUE a, VALUE *slot, VALUE b,
+ RBIMPL_ATTR_MAYBE_UNUSED()
+ const char *filename,
+ RBIMPL_ATTR_MAYBE_UNUSED()
+ int line)
+{
+#ifdef RGENGC_LOGGING_WRITE
+ RGENGC_LOGGING_WRITE(a, slot, b, filename, line);
+#endif
+
+ *slot = b;
+
+ rb_obj_written(a, RUBY_Qundef /* ignore `oldv' now */, b, filename, line);
+ return a;
+}
+
+#endif /* RBIMPL_GC_H */
diff --git a/include/ruby/internal/glob.h b/include/ruby/internal/glob.h
new file mode 100644
index 0000000000..adbccbdc5e
--- /dev/null
+++ b/include/ruby/internal/glob.h
@@ -0,0 +1,113 @@
+#ifndef RBIMPL_GLOB_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_GLOB_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Declares ::rb_glob().
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Type of a glob callback function. Called every time glob scans a path.
+ *
+ * @param[in] path The path in question.
+ * @param[in] arg The argument passed to rb_glob().
+ * @param[in] enc Encoding of the path.
+ * @retval -1 Not enough memory to do the operation.
+ * @retval 0 Operation successful.
+ * @retval otherwise Opaque exception state.
+ * @note You can use rb_protect() to generate the return value.
+ *
+ * @internal
+ *
+ * This is a wrong design. Type of `enc` should have been `rb_encoding*`
+ * instead of just `void*`. But we cannot change the API any longer.
+ *
+ * Though not a part of our public API, the "opaque exception state" is in fact
+ * an enum ruby_tag_type. You can see the potential "otherwise" values by
+ * looking at vm_core.h.
+ */
+typedef int ruby_glob_func(const char *path, VALUE arg, void *enc);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * The "glob" operator. Expands the given pattern against the actual local
+ * filesystem, then iterates over the expanded filenames by calling the
+ * callback function.
+ *
+ * @param[in] pattern A glob pattern.
+ * @param[in] func Identical to ruby_glob_func, except it can raise
+ * exceptions instead of returning opaque state.
+ * @param[in] arg Extra argument passed to func.
+ * @exception rb_eException Can propagate what `func` raises.
+ * @note The language accepted as the pattern is not a regular
+ * expression. It resembles shell's glob.
+ */
+void rb_glob(const char *pattern, void (*func)(const char *path, VALUE arg, void *enc), VALUE arg);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_glob(), except it returns opaque exception states instead of
+ * raising exceptions.
+ *
+ * @param[in] pattern A glob pattern.
+ * @param[in] flags No, you are not allowed to use this. Just pass 0.
+ * @param[in] func A callback function.
+ * @param[in] arg Extra argument passed to func.
+ * @return Return value of `func`.
+ *
+ * @internal
+ *
+ * This function is completely broken by design... Not only is there no sane
+ * way to pass flags, but there also is no sane way to know what a return value
+ * is meant to be.
+ *
+ * Though not a part of our public API, and @shyouhei thinks it's a failure not
+ * to be a public API, the flags can be `FNM_EXTGLOB`, `FNM_DOTMATCH` etc.
+ * Look at dir.c for the list.
+ *
+ * Though not a part of our public API, the return value is in fact an
+ * enum ruby_tag_type. You can see the potential values by looking at
+ * vm_core.h.
+ */
+int ruby_glob(const char *pattern, int flags, ruby_glob_func *func, VALUE arg);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to ruby_glob(), @shyouhei currently suspects. Historically you
+ * had to call this function instead of ruby_glob() if the pattern included
+ * "{x,y,...}" syntax. However since commit 0f63d961169989a7f6dcf7c0487fe29da,
+ * ruby_glob() also supports that syntax. It seems as of writing these two
+ * functions provide basically the same functionality in a different
+ * implementation. Is this analysis right? Correct me! :FIXME:
+ *
+ * @param[in] pattern A glob pattern.
+ * @param[in] flags No, you are not allowed to use this. Just pass 0.
+ * @param[in] func A callback function.
+ * @param[in] arg Extra argument passed to func.
+ * @return Return value of `func`.
+ */
+int ruby_brace_glob(const char *pattern, int flags, ruby_glob_func *func, VALUE arg);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_GLOB_H */
diff --git a/include/ruby/internal/globals.h b/include/ruby/internal/globals.h
new file mode 100644
index 0000000000..9beb215c0c
--- /dev/null
+++ b/include/ruby/internal/globals.h
@@ -0,0 +1,211 @@
+#ifndef RBIMPL_GLOBALS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_GLOBALS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Ruby-level global variables / constants, visible from C.
+ */
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+
+/**
+ * @defgroup object Core objects and their operations
+ *
+ * @internal
+ *
+ * There are several questionable constants listed in this header file. They
+ * are intentionally left untouched for purely academic backwards compatibility
+ * concerns. But for instance do any one of 3rd party extension libraries even
+ * need to know that there is NameError::Message?
+ *
+ * @endinternal
+ *
+ * @{
+ */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define RUBY_INTEGER_UNIFICATION 1
+
+RUBY_EXTERN VALUE rb_mKernel; /**< `Kernel` module. */
+RUBY_EXTERN VALUE rb_mComparable; /**< `Comparable` module. */
+RUBY_EXTERN VALUE rb_mEnumerable; /**< `Enumerable` module. */
+RUBY_EXTERN VALUE rb_mErrno; /**< `Errno` module. */
+RUBY_EXTERN VALUE rb_mFileTest; /**< `FileTest` module. */
+RUBY_EXTERN VALUE rb_mGC; /**< `GC` module. */
+RUBY_EXTERN VALUE rb_mMath; /**< `Math` module. */
+RUBY_EXTERN VALUE rb_mProcess; /**< `Process` module. */
+RUBY_EXTERN VALUE rb_mWaitReadable; /**< `IO::WaitReadable` module. */
+RUBY_EXTERN VALUE rb_mWaitWritable; /**< `IO::WaitReadable` module. */
+
+RUBY_EXTERN VALUE rb_cBasicObject; /**< `BasicObject` class. */
+RUBY_EXTERN VALUE rb_cObject; /**< `Object` class. */
+RUBY_EXTERN VALUE rb_cArray; /**< `Array` class. */
+RUBY_EXTERN VALUE rb_cBinding; /**< `Binding` class. */
+RUBY_EXTERN VALUE rb_cBox; /**< `Ruby::Box` class. */
+RUBY_EXTERN VALUE rb_cClass; /**< `Class` class. */
+RUBY_EXTERN VALUE rb_cDir; /**< `Dir` class. */
+RUBY_EXTERN VALUE rb_cEncoding; /**< `Encoding` class. */
+RUBY_EXTERN VALUE rb_cEnumerator; /**< `Enumerator` class. */
+RUBY_EXTERN VALUE rb_cFalseClass; /**< `FalseClass` class. */
+RUBY_EXTERN VALUE rb_cFile; /**< `File` class. */
+RUBY_EXTERN VALUE rb_cComplex; /**< `Complex` class. */
+RUBY_EXTERN VALUE rb_cFloat; /**< `Float` class. */
+RUBY_EXTERN VALUE rb_cHash; /**< `Hash` class. */
+RUBY_EXTERN VALUE rb_cIO; /**< `IO` class. */
+RUBY_EXTERN VALUE rb_cInteger; /**< `Module` class. */
+RUBY_EXTERN VALUE rb_cMatch; /**< `MatchData` class. */
+RUBY_EXTERN VALUE rb_cMethod; /**< `Method` class. */
+RUBY_EXTERN VALUE rb_cModule; /**< `Module` class. */
+RUBY_EXTERN VALUE rb_cRefinement; /**< `Refinement` class. */
+RUBY_EXTERN VALUE rb_cNameErrorMesg; /**< `NameError::Message` class. */
+RUBY_EXTERN VALUE rb_cNilClass; /**< `NilClass` class. */
+RUBY_EXTERN VALUE rb_cNumeric; /**< `Numeric` class. */
+RUBY_EXTERN VALUE rb_cProc; /**< `Proc` class. */
+RUBY_EXTERN VALUE rb_cRandom; /**< `Random` class. */
+RUBY_EXTERN VALUE rb_cRange; /**< `Range` class. */
+RUBY_EXTERN VALUE rb_cRational; /**< `Rational` class. */
+RUBY_EXTERN VALUE rb_cRegexp; /**< `Regexp` class. */
+RUBY_EXTERN VALUE rb_cSet; /**< `Set` class. */
+RUBY_EXTERN VALUE rb_cStat; /**< `File::Stat` class. */
+RUBY_EXTERN VALUE rb_cString; /**< `String` class. */
+RUBY_EXTERN VALUE rb_cStruct; /**< `Struct` class. */
+RUBY_EXTERN VALUE rb_cSymbol; /**< `Symbol` class. */
+RUBY_EXTERN VALUE rb_cThread; /**< `Thread` class. */
+RUBY_EXTERN VALUE rb_cTime; /**< `Time` class. */
+RUBY_EXTERN VALUE rb_cTrueClass; /**< `TrueClass` class. */
+RUBY_EXTERN VALUE rb_cUnboundMethod; /**< `UnboundMethod` class. */
+
+/**
+ * @}
+ * @addtogroup exception
+ * @{
+ */
+
+RUBY_EXTERN VALUE rb_eException; /**< Mother of all exceptions. */
+RUBY_EXTERN VALUE rb_eStandardError; /**< `StandardError` exception. */
+RUBY_EXTERN VALUE rb_eSystemExit; /**< `SystemExit` exception. */
+RUBY_EXTERN VALUE rb_eInterrupt; /**< `Interrupt` exception. */
+RUBY_EXTERN VALUE rb_eSignal; /**< `SignalException` exception. */
+RUBY_EXTERN VALUE rb_eFatal; /**< `fatal` exception. */
+RUBY_EXTERN VALUE rb_eArgError; /**< `ArgumentError` exception. */
+RUBY_EXTERN VALUE rb_eEOFError; /**< `EOFError` exception. */
+RUBY_EXTERN VALUE rb_eIndexError; /**< `IndexError` exception. */
+RUBY_EXTERN VALUE rb_eStopIteration; /**< `StopIteration` exception. */
+RUBY_EXTERN VALUE rb_eKeyError; /**< `KeyError` exception. */
+RUBY_EXTERN VALUE rb_eRangeError; /**< `RangeError` exception. */
+RUBY_EXTERN VALUE rb_eIOError; /**< `IOError` exception. */
+RUBY_EXTERN VALUE rb_eRuntimeError; /**< `RuntimeError` exception. */
+RUBY_EXTERN VALUE rb_eFrozenError; /**< `FrozenError` exception. */
+RUBY_EXTERN VALUE rb_eSecurityError; /**< `SecurityError` exception. */
+RUBY_EXTERN VALUE rb_eSystemCallError; /**< `SystemCallError` exception. */
+RUBY_EXTERN VALUE rb_eThreadError; /**< `ThreadError` exception. */
+RUBY_EXTERN VALUE rb_eTypeError; /**< `TypeError` exception. */
+RUBY_EXTERN VALUE rb_eZeroDivError; /**< `ZeroDivisionError` exception. */
+RUBY_EXTERN VALUE rb_eNotImpError; /**< `NotImplementedError` exception. */
+RUBY_EXTERN VALUE rb_eNoMemError; /**< `NoMemoryError` exception. */
+RUBY_EXTERN VALUE rb_eNoMethodError; /**< `NoMethodError` exception. */
+RUBY_EXTERN VALUE rb_eFloatDomainError; /**< `FloatDomainError` exception. */
+RUBY_EXTERN VALUE rb_eLocalJumpError; /**< `LocalJumpError` exception. */
+RUBY_EXTERN VALUE rb_eSysStackError; /**< `SystemStackError` exception. */
+RUBY_EXTERN VALUE rb_eRegexpError; /**< `RegexpError` exception. */
+RUBY_EXTERN VALUE rb_eEncodingError; /**< `EncodingError` exception. */
+RUBY_EXTERN VALUE rb_eEncCompatError; /**< `Encoding::CompatibilityError` exception. */
+RUBY_EXTERN VALUE rb_eNoMatchingPatternError; /**< `NoMatchingPatternError` exception. */
+RUBY_EXTERN VALUE rb_eNoMatchingPatternKeyError; /**< `NoMatchingPatternKeyError` exception. */
+
+RUBY_EXTERN VALUE rb_eScriptError; /**< `ScriptError` exception. */
+RUBY_EXTERN VALUE rb_eNameError; /**< `NameError` exception. */
+RUBY_EXTERN VALUE rb_eSyntaxError; /**< `SyntaxError` exception. */
+RUBY_EXTERN VALUE rb_eLoadError; /**< `LoadError` exception. */
+
+RUBY_EXTERN VALUE rb_eMathDomainError; /**< `Math::DomainError` exception. */
+
+/**
+ * @}
+ * @addtogroup object
+ * @{
+ */
+
+RUBY_EXTERN VALUE rb_stdin; /**< `STDIN` constant. */
+RUBY_EXTERN VALUE rb_stdout; /**< `STDOUT` constant. */
+RUBY_EXTERN VALUE rb_stderr; /**< `STDERR` constant. */
+
+RBIMPL_ATTR_PURE()
+/**
+ * Object to class mapping function. Every object have its class. This
+ * function obtains that.
+ *
+ * @param[in] obj Target object to query.
+ * @return The class of the given object.
+ *
+ * @internal
+ *
+ * This function is a super-duper hot path. Optimised targeting modern C
+ * compilers and x86_64 architecture.
+ */
+static inline VALUE
+rb_class_of(VALUE obj)
+{
+ if (! RB_SPECIAL_CONST_P(obj)) {
+ return RBASIC_CLASS(obj);
+ }
+ else if (obj == RUBY_Qfalse) {
+ return rb_cFalseClass;
+ }
+ else if (obj == RUBY_Qnil) {
+ return rb_cNilClass;
+ }
+ else if (obj == RUBY_Qtrue) {
+ return rb_cTrueClass;
+ }
+ else if (RB_FIXNUM_P(obj)) {
+ return rb_cInteger;
+ }
+ else if (RB_STATIC_SYM_P(obj)) {
+ return rb_cSymbol;
+ }
+ else if (RB_FLONUM_P(obj)) {
+ return rb_cFloat;
+ }
+
+#if !RUBY_DEBUG
+ RBIMPL_UNREACHABLE_RETURN(Qfalse);
+#else
+ RUBY_ASSERT_FAIL("unexpected type");
+#endif
+}
+
+#define CLASS_OF rb_class_of /**< @old{rb_class_of} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/** @} */
+
+#endif /* RBIMPL_GLOBALS_H */
diff --git a/include/ruby/internal/has/attribute.h b/include/ruby/internal/has/attribute.h
new file mode 100644
index 0000000000..f068a65caf
--- /dev/null
+++ b/include/ruby/internal/has/attribute.h
@@ -0,0 +1,163 @@
+#ifndef RBIMPL_HAS_ATTRIBUTE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_HAS_ATTRIBUTE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_HAS_ATTRIBUTE.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/compiler_since.h"
+
+#if defined(__has_attribute)
+# if __has_attribute(pure) || RBIMPL_COMPILER_IS(GCC)
+# /* FreeBSD's <sys/cdefs.h> defines its own *broken* version of
+# * __has_attribute. Cygwin copied that content to be a victim of the
+# * broken-ness. We don't take them into account. */
+# define RBIMPL_HAVE___HAS_ATTRIBUTE 1
+# endif
+#endif
+
+/** Wraps (or simulates) `__has_attribute`. */
+#if defined(RBIMPL_HAVE___HAS_ATTRIBUTE)
+# define RBIMPL_HAS_ATTRIBUTE(_) __has_attribute(_)
+
+#elif RBIMPL_COMPILER_IS(GCC)
+# /* GCC <= 4 lack __has_attribute predefined macro, while have attributes
+# * themselves. We can simulate the macro like the following: */
+# define RBIMPL_HAS_ATTRIBUTE(_) (RBIMPL_HAS_ATTRIBUTE_ ## _)
+# define RBIMPL_HAS_ATTRIBUTE_aligned RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# define RBIMPL_HAS_ATTRIBUTE_alloc_size RBIMPL_COMPILER_SINCE(GCC, 4, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_artificial RBIMPL_COMPILER_SINCE(GCC, 4, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_always_inline RBIMPL_COMPILER_SINCE(GCC, 3, 1, 0)
+# define RBIMPL_HAS_ATTRIBUTE_cdecl RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# define RBIMPL_HAS_ATTRIBUTE_cold RBIMPL_COMPILER_SINCE(GCC, 4, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_const RBIMPL_COMPILER_SINCE(GCC, 2, 6, 0)
+# define RBIMPL_HAS_ATTRIBUTE_deprecated RBIMPL_COMPILER_SINCE(GCC, 3, 1, 0)
+# define RBIMPL_HAS_ATTRIBUTE_dllexport RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# define RBIMPL_HAS_ATTRIBUTE_dllimport RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# define RBIMPL_HAS_ATTRIBUTE_error RBIMPL_COMPILER_SINCE(GCC, 4, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_format RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# define RBIMPL_HAS_ATTRIBUTE_hot RBIMPL_COMPILER_SINCE(GCC, 4, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_leaf RBIMPL_COMPILER_SINCE(GCC, 4, 6, 0)
+# define RBIMPL_HAS_ATTRIBUTE_malloc RBIMPL_COMPILER_SINCE(GCC, 3, 0, 0)
+# define RBIMPL_HAS_ATTRIBUTE_no_address_safety_analysis RBIMPL_COMPILER_SINCE(GCC, 4, 8, 0)
+# define RBIMPL_HAS_ATTRIBUTE_no_sanitize_address RBIMPL_COMPILER_SINCE(GCC, 4, 8, 0)
+# define RBIMPL_HAS_ATTRIBUTE_no_sanitize_undefined RBIMPL_COMPILER_SINCE(GCC, 4, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_noinline RBIMPL_COMPILER_SINCE(GCC, 3, 1, 0)
+# define RBIMPL_HAS_ATTRIBUTE_nonnull RBIMPL_COMPILER_SINCE(GCC, 3, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_noreturn RBIMPL_COMPILER_SINCE(GCC, 2, 5, 0)
+# define RBIMPL_HAS_ATTRIBUTE_nothrow RBIMPL_COMPILER_SINCE(GCC, 3, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_pure RBIMPL_COMPILER_SINCE(GCC, 2,96, 0)
+# define RBIMPL_HAS_ATTRIBUTE_returns_nonnull RBIMPL_COMPILER_SINCE(GCC, 4, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_returns_twice RBIMPL_COMPILER_SINCE(GCC, 4, 1, 0)
+# define RBIMPL_HAS_ATTRIBUTE_stdcall RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# define RBIMPL_HAS_ATTRIBUTE_unused RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# define RBIMPL_HAS_ATTRIBUTE_visibility RBIMPL_COMPILER_SINCE(GCC, 3, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_warn_unused_result RBIMPL_COMPILER_SINCE(GCC, 3, 4, 0)
+# define RBIMPL_HAS_ATTRIBUTE_warning RBIMPL_COMPILER_SINCE(GCC, 4, 3, 0)
+# define RBIMPL_HAS_ATTRIBUTE_weak RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# /* Note that "0, 0, 0" might be inaccurate. */
+
+#elif RBIMPL_COMPILER_IS(SunPro)
+# /* Oracle Solaris Studio 12.4 (cc version 5.11) introduced __has_attribute.
+# * Before that, following attributes were available. */
+# /* See https://docs.oracle.com/cd/F24633_01/index.html */
+# define RBIMPL_HAS_ATTRIBUTE(_) (RBIMPL_HAS_ATTRIBUTE_ ## _)
+# define RBIMPL_HAS_ATTRIBUTE_alias RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_aligned RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_always_inline RBIMPL_COMPILER_SINCE(SunPro, 5, 10, 0)
+# define RBIMPL_HAS_ATTRIBUTE_const RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_constructor RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_destructor RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_malloc RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_noinline RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_noreturn RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_packed RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_pure RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_returns_twice RBIMPL_COMPILER_SINCE(SunPro, 5, 10, 0)
+# define RBIMPL_HAS_ATTRIBUTE_vector_size RBIMPL_COMPILER_SINCE(SunPro, 5, 10, 0)
+# define RBIMPL_HAS_ATTRIBUTE_visibility RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+# define RBIMPL_HAS_ATTRIBUTE_weak RBIMPL_COMPILER_SINCE(SunPro, 5, 9, 0)
+
+#elif defined (_MSC_VER)
+# define RBIMPL_HAS_ATTRIBUTE(_) 0
+# /* Fallback below doesn't work: see win32/Makefile.sub */
+
+#else
+# /* Take config.h definition when available. */
+# define RBIMPL_HAS_ATTRIBUTE(_) ((RBIMPL_HAS_ATTRIBUTE_ ## _)+0)
+# ifdef ALWAYS_INLINE
+# define RBIMPL_HAS_ATTRIBUTE_always_inline 1
+# endif
+# ifdef FUNC_CDECL
+# define RBIMPL_HAS_ATTRIBUTE_cdecl 1
+# endif
+# ifdef CONSTFUNC
+# define RBIMPL_HAS_ATTRIBUTE_const 1
+# endif
+# ifdef DEPRECATED
+# define RBIMPL_HAS_ATTRIBUTE_deprecated 1
+# endif
+# ifdef ERRORFUNC
+# define RBIMPL_HAS_ATTRIBUTE_error 1
+# endif
+# ifdef FUNC_FASTCALL
+# define RBIMPL_HAS_ATTRIBUTE_fastcall 1
+# endif
+# ifdef PUREFUNC
+# define RBIMPL_HAS_ATTRIBUTE_pure 1
+# endif
+# ifdef NO_ADDRESS_SAFETY_ANALYSIS
+# define RBIMPL_HAS_ATTRIBUTE_no_address_safety_analysis 1
+# endif
+# ifdef NO_SANITIZE
+# define RBIMPL_HAS_ATTRIBUTE_no_sanitize 1
+# endif
+# ifdef NO_SANITIZE_ADDRESS
+# define RBIMPL_HAS_ATTRIBUTE_no_sanitize_address 1
+# endif
+# ifdef NOINLINE
+# define RBIMPL_HAS_ATTRIBUTE_noinline 1
+# endif
+# ifdef RBIMPL_FUNC_NONNULL
+# define RBIMPL_HAS_ATTRIBUTE_nonnull 1
+# endif
+# ifdef NORETURN
+# define RBIMPL_HAS_ATTRIBUTE_noreturn 1
+# endif
+# ifdef FUNC_OPTIMIZED
+# define RBIMPL_HAS_ATTRIBUTE_optimize 1
+# endif
+# ifdef FUNC_STDCALL
+# define RBIMPL_HAS_ATTRIBUTE_stdcall 1
+# endif
+# ifdef MAYBE_UNUSED
+# define RBIMPL_HAS_ATTRIBUTE_unused 1
+# endif
+# ifdef WARN_UNUSED_RESULT
+# define RBIMPL_HAS_ATTRIBUTE_warn_unused_result 1
+# endif
+# ifdef WARNINGFUNC
+# define RBIMPL_HAS_ATTRIBUTE_warning 1
+# endif
+# ifdef WEAK
+# define RBIMPL_HAS_ATTRIBUTE_weak 1
+# endif
+#endif
+
+#endif /* RBIMPL_HAS_ATTRIBUTE_H */
diff --git a/include/ruby/internal/has/builtin.h b/include/ruby/internal/has/builtin.h
new file mode 100644
index 0000000000..8e7fb173d8
--- /dev/null
+++ b/include/ruby/internal/has/builtin.h
@@ -0,0 +1,121 @@
+#ifndef RBIMPL_HAS_BUILTIN_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_HAS_BUILTIN_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_HAS_BUILTIN.
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/compiler_since.h"
+
+#if defined(__has_builtin)
+# if RBIMPL_COMPILER_IS(Intel)
+# /* :TODO: Intel C Compiler has __has_builtin (since 19.1 maybe?), and is
+# * reportedly broken. We have to skip them. However the situation can
+# * change. They might improve someday. We need to revisit here later. */
+# elif RBIMPL_COMPILER_IS(GCC) && ! __has_builtin(__builtin_alloca)
+# /* FreeBSD's <sys/cdefs.h> defines its own *broken* version of
+# * __has_builtin. Cygwin copied that content to be a victim of the
+# * broken-ness. We don't take them into account. */
+# else
+# define RBIMPL_HAVE___HAS_BUILTIN 1
+# endif
+#endif
+
+/** Wraps (or simulates) `__has_builtin`. */
+#if defined(RBIMPL_HAVE___HAS_BUILTIN)
+# define RBIMPL_HAS_BUILTIN(_) __has_builtin(_)
+
+#elif RBIMPL_COMPILER_IS(GCC)
+# /* :FIXME: Historically GCC has had tons of builtins, but it implemented
+# * __has_builtin only since GCC 10. This section can be made more
+# * granular. */
+# /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970 */
+# define RBIMPL_HAS_BUILTIN(_) (RBIMPL_HAS_BUILTIN_ ## _)
+# define RBIMPL_HAS_BUILTIN___builtin_add_overflow RBIMPL_COMPILER_SINCE(GCC, 5, 1, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_add_overflow_p RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_alloca RBIMPL_COMPILER_SINCE(GCC, 0, 0, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_alloca_with_align RBIMPL_COMPILER_SINCE(GCC, 6, 1, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_assume 0
+# /* See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52624 for bswap16. */
+# define RBIMPL_HAS_BUILTIN___builtin_bswap16 RBIMPL_COMPILER_SINCE(GCC, 4, 8, 0)
+#ifndef __OpenBSD__
+# define RBIMPL_HAS_BUILTIN___builtin_bswap32 RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_bswap64 RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+#endif
+# define RBIMPL_HAS_BUILTIN___builtin_clz RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_clzl RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_clzll RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_constant_p RBIMPL_COMPILER_SINCE(GCC, 2,95, 3)
+# define RBIMPL_HAS_BUILTIN___builtin_ctz RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_ctzl RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_ctzll RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_expect RBIMPL_COMPILER_SINCE(GCC, 3, 0, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_mul_overflow RBIMPL_COMPILER_SINCE(GCC, 5, 1, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_mul_overflow_p RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_popcount RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_popcountl RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_popcountll RBIMPL_COMPILER_SINCE(GCC, 3, 6, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_rotateleft32 0
+# define RBIMPL_HAS_BUILTIN___builtin_rotateleft64 0
+# define RBIMPL_HAS_BUILTIN___builtin_rotateright32 0
+# define RBIMPL_HAS_BUILTIN___builtin_rotateright64 0
+# define RBIMPL_HAS_BUILTIN___builtin_sub_overflow RBIMPL_COMPILER_SINCE(GCC, 5, 1, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_sub_overflow_p RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0)
+# define RBIMPL_HAS_BUILTIN___builtin_unreachable RBIMPL_COMPILER_SINCE(GCC, 4, 5, 0)
+# /* Note that "0, 0, 0" might be inaccurate. */
+
+#else
+# /* Take config.h definition when available */
+# define RBIMPL_HAS_BUILTIN(_) ((RBIMPL_HAS_BUILTIN_ ## _)+0)
+# define RBIMPL_HAS_BUILTIN___builtin_add_overflow HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW
+# define RBIMPL_HAS_BUILTIN___builtin_add_overflow_p HAVE_BUILTIN___BUILTIN_ADD_OVERFLOW_P
+# define RBIMPL_HAS_BUILTIN___builtin_alloca 0
+# define RBIMPL_HAS_BUILTIN___builtin_alloca_with_align HAVE_BUILTIN___BUILTIN_ALLOCA_WITH_ALIGN
+# define RBIMPL_HAS_BUILTIN___builtin_assume 0
+# define RBIMPL_HAS_BUILTIN___builtin_assume_aligned HAVE_BUILTIN___BUILTIN_ASSUME_ALIGNED
+# define RBIMPL_HAS_BUILTIN___builtin_bswap16 HAVE_BUILTIN___BUILTIN_BSWAP16
+# define RBIMPL_HAS_BUILTIN___builtin_bswap32 HAVE_BUILTIN___BUILTIN_BSWAP32
+# define RBIMPL_HAS_BUILTIN___builtin_bswap64 HAVE_BUILTIN___BUILTIN_BSWAP64
+# define RBIMPL_HAS_BUILTIN___builtin_clz HAVE_BUILTIN___BUILTIN_CLZ
+# define RBIMPL_HAS_BUILTIN___builtin_clzl HAVE_BUILTIN___BUILTIN_CLZL
+# define RBIMPL_HAS_BUILTIN___builtin_clzll HAVE_BUILTIN___BUILTIN_CLZLL
+# define RBIMPL_HAS_BUILTIN___builtin_constant_p HAVE_BUILTIN___BUILTIN_CONSTANT_P
+# define RBIMPL_HAS_BUILTIN___builtin_ctz HAVE_BUILTIN___BUILTIN_CTZ
+# define RBIMPL_HAS_BUILTIN___builtin_ctzl 0
+# define RBIMPL_HAS_BUILTIN___builtin_ctzll HAVE_BUILTIN___BUILTIN_CTZLL
+# define RBIMPL_HAS_BUILTIN___builtin_expect HAVE_BUILTIN___BUILTIN_EXPECT
+# define RBIMPL_HAS_BUILTIN___builtin_mul_overflow HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW
+# define RBIMPL_HAS_BUILTIN___builtin_mul_overflow_p HAVE_BUILTIN___BUILTIN_MUL_OVERFLOW_P
+# define RBIMPL_HAS_BUILTIN___builtin_popcount HAVE_BUILTIN___BUILTIN_POPCOUNT
+# define RBIMPL_HAS_BUILTIN___builtin_popcountl 0
+# define RBIMPL_HAS_BUILTIN___builtin_rotateleft32 0
+# define RBIMPL_HAS_BUILTIN___builtin_rotateleft64 0
+# define RBIMPL_HAS_BUILTIN___builtin_rotateright32 0
+# define RBIMPL_HAS_BUILTIN___builtin_rotateright64 0
+# define RBIMPL_HAS_BUILTIN___builtin_popcountll HAVE_BUILTIN___BUILTIN_POPCOUNTLL
+# define RBIMPL_HAS_BUILTIN___builtin_sub_overflow HAVE_BUILTIN___BUILTIN_SUB_OVERFLOW
+# define RBIMPL_HAS_BUILTIN___builtin_sub_overflow_p HAVE_BUILTIN___BUILTIN_SUB_OVERFLOW_P
+# if defined(HAVE___BUILTIN_UNREACHABLE)
+# define RBIMPL_HAS_BUILTIN___builtin_unreachable 1
+# else
+# define RBIMPL_HAS_BUILTIN___builtin_unreachable 0
+# endif
+#endif
+
+#endif /* RBIMPL_HAS_BUILTIN_H */
diff --git a/include/ruby/internal/has/c_attribute.h b/include/ruby/internal/has/c_attribute.h
new file mode 100644
index 0000000000..69b0f402cd
--- /dev/null
+++ b/include/ruby/internal/has/c_attribute.h
@@ -0,0 +1,50 @@
+#ifndef RBIMPL_HAS_C_ATTRIBUTE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_HAS_C_ATTRIBUTE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_HAS_C_ATTRIBUTE.
+ */
+
+#include "ruby/internal/has/extension.h"
+#include "ruby/internal/has/warning.h"
+
+/** Wraps (or simulates) `__has_c_attribute`. */
+#if defined(__cplusplus)
+# /* Makes no sense. */
+# define RBIMPL_HAS_C_ATTRIBUTE(_) 0
+
+#elif RBIMPL_HAS_EXTENSION(c_attributes)
+# /* Hmm. It seems Clang 17 has this macro defined even when -std=c99 mode,
+# * _and_ fails to compile complaining that attributes are C2X feature. We
+# * need to work around this nonsense. */
+# define RBIMPL_HAS_C_ATTRIBUTE(_) __has_c_attribute(_)
+
+#elif RBIMPL_HAS_WARNING("-Wc2x-extensions")
+# define RBIMPL_HAS_C_ATTRIBUTE(_) 0
+
+#elif defined(__has_c_attribute)
+# define RBIMPL_HAS_C_ATTRIBUTE(_) __has_c_attribute(_)
+
+#else
+# /* As of writing everything that lacks __has_c_attribute also completely
+# * lacks C2x attributes as well. Might change in future? */
+# define RBIMPL_HAS_C_ATTRIBUTE(_) 0
+#endif
+
+#endif /* RBIMPL_HAS_C_ATTRIBUTE_H */
diff --git a/include/ruby/internal/has/cpp_attribute.h b/include/ruby/internal/has/cpp_attribute.h
new file mode 100644
index 0000000000..6a393b1a24
--- /dev/null
+++ b/include/ruby/internal/has/cpp_attribute.h
@@ -0,0 +1,86 @@
+#ifndef RBIMPL_HAS_CPP_ATTRIBUTE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_HAS_CPP_ATTRIBUTE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_HAS_CPP_ATTRIBUTE.
+ */
+#include "ruby/internal/compiler_is.h"
+#include "ruby/internal/compiler_since.h"
+
+/** @cond INTERNAL_MACRO */
+#if RBIMPL_COMPILER_IS(SunPro)
+# /* Oracle Developer Studio 12.5's C++ preprocessor is reportedly broken. We
+# * could simulate __has_cpp_attribute like below, but don't know the exact
+# * list of which version supported which attribute. Just kill everything for
+# * now. If you can please :FIXME: */
+# /* https://unicode-org.atlassian.net/browse/ICU-12893 */
+# /* https://github.com/boostorg/config/pull/95 */
+# define RBIMPL_HAS_CPP_ATTRIBUTE0(_) 0
+
+#elif defined(__has_cpp_attribute)
+# define RBIMPL_HAS_CPP_ATTRIBUTE0(_) __has_cpp_attribute(_)
+
+#elif RBIMPL_COMPILER_IS(MSVC)
+# /* MSVC has never updated its __cplusplus since forever (unless specified
+# * explicitly by a compiler flag). They also lack __has_cpp_attribute until
+# * 2019. However, they do have attributes since 2015 or so. */
+# /* https://docs.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance */
+# define RBIMPL_HAS_CPP_ATTRIBUTE0(_) (RBIMPL_HAS_CPP_ATTRIBUTE_ ## _)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_noreturn 200809 * RBIMPL_COMPILER_SINCE(MSVC, 19, 00, 0)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_carries_dependency 200809 * RBIMPL_COMPILER_SINCE(MSVC, 19, 00, 0)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_deprecated 201309 * RBIMPL_COMPILER_SINCE(MSVC, 19, 10, 0)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_fallthrough 201603 * RBIMPL_COMPILER_SINCE(MSVC, 19, 10, 0)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_maybe_unused 201603 * RBIMPL_COMPILER_SINCE(MSVC, 19, 11, 0)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_nodiscard 201603 * RBIMPL_COMPILER_SINCE(MSVC, 19, 11, 0)
+
+#elif RBIMPL_COMPILER_BEFORE(Clang, 3, 6, 0)
+# /* Clang 3.6.0 introduced __has_cpp_attribute. Prior to that following
+# * attributes were already there. */
+# /* https://clang.llvm.org/cxx_status.html */
+# define RBIMPL_HAS_CPP_ATTRIBUTE0(_) (RBIMPL_HAS_CPP_ATTRIBUTE_ ## _)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_noreturn 200809 * RBIMPL_COMPILER_SINCE(Clang, 3, 3, 0)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_deprecated 201309 * RBIMPL_COMPILER_SINCE(Clang, 3, 4, 0)
+
+#elif RBIMPL_COMPILER_BEFORE(GCC, 5, 0, 0)
+# /* GCC 5+ have __has_cpp_attribute, while 4.x had following attributes. */
+# /* https://gcc.gnu.org/projects/cxx-status.html */
+# define RBIMPL_HAS_CPP_ATTRIBUTE0(_) (RBIMPL_HAS_CPP_ATTRIBUTE_ ## _)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_noreturn 200809 * RBIMPL_COMPILER_SINCE(GCC, 4, 8, 0)
+# define RBIMPL_HAS_CPP_ATTRIBUTE_deprecated 201309 * RBIMPL_COMPILER_SINCE(GCC, 4, 9, 0)
+
+#else
+# /* :FIXME:
+# * Candidate compilers to list here:
+# * - icpc: They have __INTEL_CXX11_MODE__.
+# */
+# define RBIMPL_HAS_CPP_ATTRIBUTE0(_) 0
+#endif
+/** @endcond */
+
+/** Wraps (or simulates) `__has_cpp_attribute`. */
+#if ! defined(__cplusplus)
+# /* Makes no sense. */
+# define RBIMPL_HAS_CPP_ATTRIBUTE(_) 0
+#else
+# /* GCC needs workarounds. See https://gcc.godbolt.org/z/jdz3pa */
+# define RBIMPL_HAS_CPP_ATTRIBUTE(_) \
+ ((RBIMPL_HAS_CPP_ATTRIBUTE0(_) <= __cplusplus) ? RBIMPL_HAS_CPP_ATTRIBUTE0(_) : 0)
+#endif
+
+#endif /* RBIMPL_HAS_CPP_ATTRIBUTE_H */
diff --git a/include/ruby/internal/has/declspec_attribute.h b/include/ruby/internal/has/declspec_attribute.h
new file mode 100644
index 0000000000..1c526daf78
--- /dev/null
+++ b/include/ruby/internal/has/declspec_attribute.h
@@ -0,0 +1,47 @@
+#ifndef RBIMPL_HAS_DECLSPEC_ATTRIBUTE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_HAS_DECLSPEC_ATTRIBUTE.
+ */
+#include "ruby/internal/compiler_since.h"
+
+/** Wraps (or simulates) `__has_declspec_attribute`. */
+#if defined(__has_declspec_attribute)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE(_) __has_declspec_attribute(_)
+#else
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE(_) (RBIMPL_HAS_DECLSPEC_ATTRIBUTE_ ## _)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_align RBIMPL_COMPILER_SINCE(MSVC, 8, 0, 0)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_deprecated RBIMPL_COMPILER_SINCE(MSVC,13, 0, 0)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_dllexport RBIMPL_COMPILER_SINCE(MSVC, 8, 0, 0)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_dllimport RBIMPL_COMPILER_SINCE(MSVC, 8, 0, 0)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_empty_bases RBIMPL_COMPILER_SINCE(MSVC,19, 0, 23918)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_noalias RBIMPL_COMPILER_SINCE(MSVC, 8, 0, 0)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_noinline RBIMPL_COMPILER_SINCE(MSVC,13, 0, 0)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_noreturn RBIMPL_COMPILER_SINCE(MSVC,11, 0, 0)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_nothrow RBIMPL_COMPILER_SINCE(MSVC, 8, 0, 0)
+# define RBIMPL_HAS_DECLSPEC_ATTRIBUTE_restrict RBIMPL_COMPILER_SINCE(MSVC,14, 0, 0)
+# /* Note that "8, 0, 0" might be inaccurate. */
+# if ! defined(__cplusplus)
+# /* Clang has this in both C/C++, but MSVC has this in C++ only.*/
+# undef RBIMPL_HAS_DECLSPEC_ATTRIBUTE_nothrow
+# endif
+#endif
+
+#endif /* RBIMPL_HAS_DECLSPEC_ATTRIBUTE_H */
diff --git a/include/ruby/internal/has/extension.h b/include/ruby/internal/has/extension.h
new file mode 100644
index 0000000000..da8c0d3cc2
--- /dev/null
+++ b/include/ruby/internal/has/extension.h
@@ -0,0 +1,33 @@
+#ifndef RBIMPL_HAS_EXTENSION_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_HAS_EXTENSION_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_HAS_EXTENSION.
+ */
+#include "ruby/internal/has/feature.h"
+
+/** Wraps (or simulates) `__has_extension`. */
+#if defined(__has_extension)
+# define RBIMPL_HAS_EXTENSION(_) __has_extension(_)
+#else
+# /* Pre-3.0 clang had __has_feature but not __has_extension. */
+# define RBIMPL_HAS_EXTENSION(_) RBIMPL_HAS_FEATURE(_)
+#endif
+
+#endif /* RBIMPL_HAS_EXTENSION_H */
diff --git a/include/ruby/internal/has/feature.h b/include/ruby/internal/has/feature.h
new file mode 100644
index 0000000000..7be8d27314
--- /dev/null
+++ b/include/ruby/internal/has/feature.h
@@ -0,0 +1,31 @@
+#ifndef RBIMPL_HAS_FEATURE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_HAS_FEATURE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_HAS_FEATURE.
+ */
+
+/** Wraps (or simulates) `__has_feature`. */
+#if defined(__has_feature)
+# define RBIMPL_HAS_FEATURE(_) __has_feature(_)
+#else
+# define RBIMPL_HAS_FEATURE(_) 0
+#endif
+
+#endif /* RBIMPL_HAS_FEATURE_H */
diff --git a/include/ruby/internal/has/warning.h b/include/ruby/internal/has/warning.h
new file mode 100644
index 0000000000..12d7db183b
--- /dev/null
+++ b/include/ruby/internal/has/warning.h
@@ -0,0 +1,31 @@
+#ifndef RBIMPL_HAS_WARNING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_HAS_WARNING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_HAS_WARNING.
+ */
+
+/** Wraps (or simulates) `__has_warning`. */
+#if defined(__has_warning)
+# define RBIMPL_HAS_WARNING(_) __has_warning(_)
+#else
+# define RBIMPL_HAS_WARNING(_) 0
+#endif
+
+#endif /* RBIMPL_HAS_WARNING_H */
diff --git a/include/ruby/internal/intern/array.h b/include/ruby/internal/intern/array.h
new file mode 100644
index 0000000000..b2cc6b132d
--- /dev/null
+++ b/include/ruby/internal/intern/array.h
@@ -0,0 +1,663 @@
+#ifndef RBIMPL_INTERN_ARRAY_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_ARRAY_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cArray.
+ */
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/noexcept.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* array.c */
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_NOALIAS()
+/**
+ * Fills the memory region with a series of ::RUBY_Qnil.
+ *
+ * @param[out] buf Buffer to squash.
+ * @param[in] len Number of objects of `buf`.
+ * @post `buf` is filled with ::RUBY_Qnil.
+ */
+void rb_mem_clear(VALUE *buf, long len)
+ RBIMPL_ATTR_NOEXCEPT(true)
+ ;
+
+/**
+ * Identical to rb_ary_new_from_values(), except it expects exactly two
+ * parameters.
+ *
+ * @param[in] car Arbitrary ruby object.
+ * @param[in] cdr Arbitrary ruby object.
+ * @return An allocated new array, of length 2, whose contents are the
+ * passed objects.
+ */
+VALUE rb_assoc_new(VALUE car, VALUE cdr);
+
+/**
+ * Try converting an object to its array representation using its `to_ary`
+ * method, if any. If there is no such thing, returns ::RUBY_Qnil.
+ *
+ * @param[in] obj Arbitrary ruby object to convert.
+ * @exception rb_eTypeError `obj.to_ary` returned something non-Array.
+ * @retval RUBY_Qnil No conversion from `obj` to array defined.
+ * @retval otherwise Converted array representation of `obj`.
+ * @see rb_io_check_io
+ * @see rb_check_string_type
+ * @see rb_check_hash_type
+ */
+VALUE rb_check_array_type(VALUE obj);
+
+/**
+ * Allocates a new, empty array.
+ *
+ * @return An allocated new array, whose length is 0.
+ */
+VALUE rb_ary_new(void);
+
+/**
+ * Identical to rb_ary_new(), except it additionally specifies how many rooms
+ * of objects it should allocate. This way you can create an array whose
+ * capacity is bigger than the length of it. If you can say that an array
+ * grows to a specific amount, this could be effective than resizing an array
+ * over and over again and again.
+ *
+ * @param[in] capa Designed capacity of the generating array.
+ * @return An empty array, whose capacity is `capa`.
+ */
+VALUE rb_ary_new_capa(long capa);
+
+/**
+ * Constructs an array from the passed objects.
+ *
+ * @param[in] n Number of passed objects.
+ * @param[in] ... Arbitrary ruby objects, filled into the returning array.
+ * @return An array of size `n`, whose contents are the passed objects.
+ */
+VALUE rb_ary_new_from_args(long n, ...);
+
+/**
+ * Identical to rb_ary_new_from_args(), except how objects are passed.
+ *
+ * @param[in] n Number of objects of `elts`.
+ * @param[in] elts Arbitrary ruby objects, filled into the returning array.
+ * @return An array of size `n`, whose contents are the passed objects.
+ */
+VALUE rb_ary_new_from_values(long n, const VALUE *elts);
+
+/**
+ * Allocates a hidden (no class) empty array.
+ *
+ * @param[in] capa Designed capacity of the array.
+ * @return A hidden, empty array.
+ * @see rb_obj_hide()
+ */
+VALUE rb_ary_hidden_new(long capa);
+#define rb_ary_tmp_new rb_ary_hidden_new
+
+/**
+ * Destroys the given array for no reason.
+ *
+ * @warning DO NOT USE IT.
+ * @warning Leave this task to our GC.
+ * @warning It was a wrong indea at the first place to let you know about it.
+ *
+ * @param[out] ary The array to be executed.
+ * @post The given array no longer exists.
+ * @note Maybe `Array#clear` could be what you want.
+ *
+ * @internal
+ *
+ * Should have moved this to `internal/array.h`.
+ */
+void rb_ary_free(VALUE ary);
+
+/**
+ * Declares that the array is about to be modified. This for instance let the
+ * array have a dedicated backend storage.
+ *
+ * @param[out] ary Array about to be modified.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @post Upon successful return the passed array is eligible to be
+ * modified.
+ */
+void rb_ary_modify(VALUE ary);
+
+/**
+ * Freeze an array, preventing further modifications. The underlying buffer may
+ * be shrunk before freezing to conserve memory.
+ *
+ * @param[out] obj Object assumed to be an array to freeze.
+ * @see RB_OBJ_FREEZE()
+ */
+VALUE rb_ary_freeze(VALUE obj);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries if the passed two arrays share the same backend storage. A use-case
+ * for knowing such property is to take a snapshot of an array (using
+ * e.g. rb_ary_replace()), then check later if that snapshot still shares the
+ * storage with the original. Taking a snapshot is ultra-cheap. If nothing
+ * happens the impact shall be minimal. But if someone modifies the original,
+ * that entity shall pay the cost of copy-on-write. You can detect that using
+ * this API.
+ *
+ * @param[in] lhs Comparison LHS.
+ * @param[in] rhs Comparison RHS.
+ * @retval RUBY_Qtrue They share the same backend storage.
+ * @retval RUBY_Qfalse They are distinct.
+ * @pre Both arguments must be of ::RUBY_T_ARRAY.
+ */
+VALUE rb_ary_shared_with_p(VALUE lhs, VALUE rhs);
+
+/**
+ * Queries element(s) of an array. This is complicated! Refer `Array#slice`
+ * document for the complete description of how it behaves.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Up to 2 objects.
+ * @param[in] ary Target array.
+ * @exception rb_eTypeError `argv` (or its part) includes non-Integer.
+ * @exception rb_eRangeError rb_cArithSeq is passed, and is OOB.
+ * @return An element (if requested), or an array of elements (if
+ * requested), or ::RUBY_Qnil (if index OOB).
+ *
+ * @internal
+ *
+ * ```rbs
+ * # "int" is ::Integer or `#to_int`, defined in builtin.rbs
+ *
+ * class ::Array[unchecked out T]
+ * def slice
+ * : (int i) -> T?
+ * | (int beg, int len) -> ::Array[T]?
+ * | (Range[int] r) -> ::Array[T]?
+ * | (ArithmeticSequence as) -> ::Array[T]? # This also raises RangeError.
+ * end
+ * ```
+ */
+VALUE rb_ary_aref(int argc, const VALUE *argv, VALUE ary);
+
+/**
+ * Obtains a part of the passed array.
+ *
+ * @param[in] ary Target array.
+ * @param[in] beg Subpart index.
+ * @param[in] len Requested length of returning array.
+ * @retval RUBY_Qnil Requested range out of bounds of `ary`.
+ * @retval otherwise An allocated new array whose contents are `ary`'s
+ * `beg` to `len`.
+ * @note Return array can be shorter than `len` when for instance
+ * `[0, 1, 2, 3]`'s 4th to 1,000,000,000th is requested.
+ */
+VALUE rb_ary_subseq(VALUE ary, long beg, long len);
+
+/**
+ * Destructively stores the passed value to the passed array's passed index.
+ * It also resizes the array's backend storage so that the requested index is
+ * not out of bounds.
+ *
+ * @param[out] ary Target array to modify.
+ * @param[in] key Where to store `val`.
+ * @param[in] val What to store at `key`.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @exception rb_eIndexError `key` is negative.
+ * @post `ary`'s `key`th position is occupied with `val`.
+ * @post Depending on `key` and previous length of `ary` this operation
+ * can also create a series of "hole" positions inside of the
+ * backend storage. They are filled with ::RUBY_Qnil.
+ */
+void rb_ary_store(VALUE ary, long key, VALUE val);
+
+/**
+ * Duplicates an array.
+ *
+ * @param[in] ary Target to duplicate.
+ * @return An allocated new array whose contents are identical to `ary`.
+ *
+ * @internal
+ *
+ * Not sure why this has to be something different from `ary_make_shared_copy`,
+ * which seems much efficient.
+ */
+VALUE rb_ary_dup(VALUE ary);
+
+/**
+ * I guess there is no use case of this function in extension libraries, but
+ * this is a routine identical to rb_ary_dup(). This makes the most sense when
+ * the passed array is formerly hidden by rb_obj_hide().
+ *
+ * @param[in] ary An array, possibly hidden.
+ * @return A duplicated new instance of ::rb_cArray.
+ */
+VALUE rb_ary_resurrect(VALUE ary);
+
+/**
+ * Force converts an object to an array. It first tries its `#to_ary` method.
+ * Takes the result if any. Otherwise creates an array of size 1 whose sole
+ * element is the passed object.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @return An array representation of `obj`.
+ * @note Unlike rb_str_to_str() which is a variant of
+ * rb_check_string_type(), rb_ary_to_ary() is not a variant of
+ * rb_check_array_type().
+ */
+VALUE rb_ary_to_ary(VALUE obj);
+
+/**
+ * Converts an array into a human-readable string. Historically its behaviour
+ * changed over time. Currently it is identical to calling `inspect` method.
+ * This behaviour is from that of python (!!) circa 2006.
+ *
+ * @param[in] ary Array to inspect.
+ * @return Recursively inspected representation of `ary`.
+ * @see `[ruby-dev:29520]`
+ */
+VALUE rb_ary_to_s(VALUE ary);
+
+/**
+ * Destructively appends multiple elements at the end of the array.
+ *
+ * @param[out] ary Where to push `train`.
+ * @param[in] train Arbitrary ruby objects to push to `ary`.
+ * @param[in] len Number of objects of `train`.
+ * @exception rb_eIndexError `len` too large.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return The passed `ary`.
+ * @post `ary` has contents from `train` appended at its end.
+ */
+VALUE rb_ary_cat(VALUE ary, const VALUE *train, long len);
+
+/**
+ * Special case of rb_ary_cat() that it adds only one element.
+ *
+ * @param[out] ary Where to push `elem`.
+ * @param[in] elem Arbitrary ruby object to push.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return The passed `ary`.
+ * @post `ary` has `elem` appended at its end.
+ */
+VALUE rb_ary_push(VALUE ary, VALUE elem);
+
+/**
+ * Destructively deletes an element from the end of the passed array and
+ * returns what was deleted.
+ *
+ * @param[out] ary Target array to modify.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return What was at the end of `ary`, or ::RUBY_Qnil if there is
+ * nothing to remove.
+ * @post `ary`'s last element, if any, is removed.
+ * @note There is no way to distinguish whether `ary` was an 1-element
+ * array whose content was ::RUBY_Qnil, or was empty.
+ */
+VALUE rb_ary_pop(VALUE ary);
+
+/**
+ * Destructively deletes an element from the beginning of the passed array and
+ * returns what was deleted. It can also be seen as a routine identical to
+ * rb_ary_pop(), except which side of the array to scrub.
+ *
+ * @param[out] ary Target array to modify.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return What was at the beginning of `ary`, or ::RUBY_Qnil if there is
+ * nothing to remove.
+ * @post `ary`'s first element, if any, is removed. As the name implies
+ * everything else remaining in `ary` gets moved towards `ary`'s
+ * beginning.
+ * @note There is no way to distinguish whether `ary` was an 1-element
+ * array whose content was ::RUBY_Qnil, or was empty.
+ */
+VALUE rb_ary_shift(VALUE ary);
+
+/**
+ * Destructively prepends the passed item at the beginning of the passed array.
+ * It can also be seen as a routine identical to rb_ary_push(), except which
+ * side of the array to modify.
+ *
+ * @param[out] ary Target array to modify.
+ * @param[in] elem Arbitrary ruby object to unshift.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return The passed `ary`.
+ * @post `ary` has `elem` prepended at this beginning.
+ */
+VALUE rb_ary_unshift(VALUE ary, VALUE elem);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries an element of an array. When passed offset is negative it counts
+ * backwards.
+ *
+ * @param[in] ary An array to look into.
+ * @param[in] off Offset (can be negative).
+ * @return ::RUBY_Qnil when `off` is out of bounds of `ary`. Otherwise
+ * what is stored at `off`-th position of `ary`.
+ * @note `ary`'s `off`-th element can happen to be ::RUBY_Qnil.
+ */
+VALUE rb_ary_entry(VALUE ary, long off);
+
+/**
+ * Iteratively yields each element of the passed array to the implicitly passed
+ * block if any. In case there is no block given, an enumerator that does the
+ * thing is generated instead.
+ *
+ * @param[in] ary Array to iterate over.
+ * @retval ary Passed block was evaluated.
+ * @retval otherwise An instance of ::rb_cEnumerator for `Array#each`.
+ */
+VALUE rb_ary_each(VALUE ary);
+
+/**
+ * Recursively stringises the elements of the passed array, flattens that
+ * result, then joins the sequence using the passed separator.
+ *
+ * @param[in] ary Target array to convert.
+ * @param[in] sep Separator. Either a string, or ::RUBY_Qnil
+ * if you want no separator.
+ * @exception rb_eArgError Infinite recursion in `ary`.
+ * @exception rb_eTypeError `sep` is not a string.
+ * @exception rb_eEncCompatError Strings do not agree with their encodings.
+ * @return An instance of ::rb_cString which concatenates stringised
+ * contents of `ary`, using `sep` as separator.
+ */
+VALUE rb_ary_join(VALUE ary, VALUE sep);
+
+/**
+ * _Destructively_ reverses the passed array in-place.
+ *
+ * @warning This is `Array#reverse!`, not `Array#reverse`.
+ * @param[out] ary Target array to modify.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return Passed `ary`.
+ * @post `ary` is reversed.
+ */
+VALUE rb_ary_reverse(VALUE ary);
+
+/**
+ * _Destructively_ rotates the passed array in-place to towards its end. The
+ * amount can be negative. Would rotate to the opposite direction then.
+ *
+ * @warning This is `Array#rotate!`, not `Array#rotate`.
+ * @param[out] ary Target array to modify.
+ * @param[in] rot Amount of rotation.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @retval RUBY_Qnil Not rotated.
+ * @retval ary Rotated.
+ * @post `ary` is rotated.
+ */
+VALUE rb_ary_rotate(VALUE ary, long rot);
+
+/**
+ * Creates a copy of the passed array, whose elements are sorted according to
+ * their `<=>` result.
+ *
+ * @param[in] ary Array to sort.
+ * @exception rb_eArgError Comparison not defined among elements.
+ * @exception rb_eRuntimeError Infinite recursion in `<=>`.
+ * @return A copy of `ary`, sorted.
+ * @note As of writing this function uses `qsort` as backend algorithm,
+ * which means the result is unstable (in terms of sort stability).
+ */
+VALUE rb_ary_sort(VALUE ary);
+
+/**
+ * Destructively sorts the passed array in-place, according to each elements'
+ * `<=>` result.
+ *
+ * @param[in] ary Target array to modify.
+ * @exception rb_eArgError Comparison not defined among elements.
+ * @exception rb_eRuntimeError Infinite recursion in `<=>`.
+ * @return Passed `ary`.
+ * @post `ary` is sorted.
+ * @note As of writing this function uses `qsort` as backend algorithm,
+ * which means the result is unstable (in terms of sort stability).
+ */
+VALUE rb_ary_sort_bang(VALUE ary);
+
+/**
+ * Destructively removes elements from the passed array, so that there would be
+ * no elements inside that satisfy `==` relationship with the passed object.
+ * Returns the last deleted element if any. But in case there was nothing to
+ * delete it gets complicated. It checks for the implicitly passed block. If
+ * there is a block the return value would be what the block evaluates to.
+ * Otherwise it resorts to ::RUBY_Qnil.
+ *
+ * @param[out] ary Target array to modify.
+ * @param[in] elem Template object to match against each element.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return What was deleted, or what was the block returned, or
+ * ::RUBY_Qnil (see above).
+ * @post All elements that have `==` relationship with `elem` are purged
+ * from `ary`. Elements shift their positions so that `ary` gets
+ * compact.
+ *
+ * @internal
+ *
+ * Internally there also is `rb_ary_delete_same`, which compares by identity.
+ */
+VALUE rb_ary_delete(VALUE ary, VALUE elem);
+
+/**
+ * Destructively removes an element which resides at the specific index of the
+ * passed array. Unlike rb_ary_stre() the index can be negative, which means
+ * the index counts backwards from the array's tail.
+ *
+ * @param[out] ary Target array to modify.
+ * @param[in] pos Position (can be negative).
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return What was deleted, or ::RUBY_Qnil in case of OOB.
+ * @post `ary`'s `pos`-th element is deleted if any.
+ * @note There is no way to distinguish whether `pos` is out of bound,
+ * or `pos` did exist but stored ::RUBY_Qnil as an ordinal value.
+ */
+VALUE rb_ary_delete_at(VALUE ary, long pos);
+
+/**
+ * Destructively removes everything form an array.
+ *
+ * @param[out] ary Target array to modify.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @return The passed `ary`.
+ * @post `ary` is an empty array.
+ */
+VALUE rb_ary_clear(VALUE ary);
+
+/**
+ * Creates a new array, concatenating the former to the latter.
+ *
+ * @param[in] lhs Source array #1.
+ * @param[in] rhs Source array #2.
+ * @exception rb_eIndexError Result array too big.
+ * @return A new array containing `rhs` concatenated to `lhs`.
+ * @note This operation doesn't commute. Don't get confused by the
+ * "plus" terminology. For historical reasons there are some
+ * noncommutative `+`s in Ruby. This is one of such things. There
+ * has been a long discussion around `+`s in programming languages.
+ *
+ * @internal
+ *
+ * rb_ary_concat() is not a destructive version of rb_ary_plus(). They raise
+ * different exceptions. Don't know why though.
+ */
+VALUE rb_ary_plus(VALUE lhs, VALUE rhs);
+
+/**
+ * Destructively appends the contents of latter into the end of former.
+ *
+ * @param[out] lhs Destination array.
+ * @param[in] rhs Source array.
+ * @exception rb_eFrozenError `lhs` is frozen.
+ * @exception rb_eIndexError Result array too big.
+ * @exception rb_eTypeError `rhs` doesn't respond to `#to_ary`.
+ * @return The passed `lhs`.
+ * @post `lhs` has contents of `rhs` appended to its end.
+ */
+VALUE rb_ary_concat(VALUE lhs, VALUE rhs);
+
+/**
+ * Looks up the passed key, assuming the passed array is an alist. An "alist"
+ * here is a list of "association"s, much like that of Emacs. Emacs has
+ * `assoc` function that behaves exactly the same as this one.
+ *
+ * ```ruby
+ * # This is an example of aliist.
+ * auto_mode_alist = [
+ * [ /\.[ch]\z/, :"c-mode" ],
+ * [ /\.[ch]pp\z/, :"c++-mode" ],
+ * [ /\.awk\z/, :"awk-mode" ],
+ * [ /\.cs\z/, :"csharp-mode" ],
+ * [ /\.go\z/, :"go-mode" ],
+ * [ /\.java\z/, :"java-mode" ],
+ * [ /\.pas\z/, :"pascal-mode" ],
+ * [ /\.rs\z/, :"rust-mode" ],
+ * [ /\.txt\z/, :"text-mode" ],
+ * ]
+ * ```
+ *
+ * This function scans the passed array looking for an element, which itself is
+ * an array, whose first element is the passed key. If no such element is
+ * found, returns ::RUBY_Qnil.
+ *
+ * Although this function expects the passed array be an array of arrays, it
+ * can happily accept non-array elements; it just ignores such things.
+ *
+ * @param[in] alist An array of arrays.
+ * @param[in] key Needle.
+ * @retval RUBY_Qnil Nothing was found.
+ * @retval otherwise An element in `alist` whose first element is in `==`
+ * relationship with `key`.
+ */
+VALUE rb_ary_assoc(VALUE alist, VALUE key);
+
+/**
+ * Identical to rb_ary_assoc(), except it scans the passed array from the
+ * opposite direction.
+ *
+ * @param[in] alist An array of arrays.
+ * @param[in] key Needle.
+ * @retval RUBY_Qnil Nothing was found.
+ * @retval otherwise An element in `alist` whose first element is in `==`
+ * relationship with `key`.
+ */
+VALUE rb_ary_rassoc(VALUE alist, VALUE key);
+
+/**
+ * Queries if the passed array has the passed entry.
+ *
+ * @param[in] ary Target array to scan.
+ * @param[in] elem Target array to find.
+ * @retval RUBY_Qfalse No element in `ary` is in `==` relationship with
+ * `elem`.
+ * @retval RUBY_Qtrue There is at least one element in `ary` which is in
+ * `==` relationship with `elem`.
+ *
+ * @internal
+ *
+ * This is the only function in the entire C API that is named using third
+ * person singular form of a verb (except #ISASCII etc., which are not our
+ * naming). The counterpart Ruby API of this function is `Array#include?`.
+ */
+VALUE rb_ary_includes(VALUE ary, VALUE elem);
+
+/**
+ * Recursively compares each elements of the two arrays one-by-one using `<=>`.
+ *
+ * @param[in] lhs Comparison LHS.
+ * @param[in] rhs Comparison RHS.
+ * @retval RUBY_Qnil `lhs` and `rhs` are not comparable.
+ * @retval -1 `lhs` is less than `rhs`.
+ * @retval 0 They are equal.
+ * @retval 1 `rhs` is less then `lhs`.
+ */
+VALUE rb_ary_cmp(VALUE lhs, VALUE rhs);
+
+/**
+ * Replaces the contents of the former object with the contents of the latter.
+ *
+ * @param[out] copy Destination object.
+ * @param[in] orig Source object.
+ * @exception rb_eTypeError `orig` has no implicit conversion to Array.
+ * @exception rb_eFrozenError `copy` is frozen.
+ * @return The passed `copy`.
+ * @post `copy`'s former components are abandoned. It now has the
+ * identical length and contents to `orig`.
+ */
+VALUE rb_ary_replace(VALUE copy, VALUE orig);
+
+/**
+ * This _was_ a generalisation of `Array#values_at`, `Struct#values_at`, and
+ * `MatchData#values_at`. It begun its life as a refactoring effort. However
+ * as Ruby evolves over time, as of writing none of aforementioned methods
+ * share their implementations at all. This function is not deprecated; still
+ * works as it has been. But it is now kind of like a rudimentum.
+ *
+ * This function takes an object, which is a receiver, and a series of
+ * "indices", which are either integers, or ranges of integers. Calls the
+ * passed callback for each of those indices, along with the receiver. This
+ * callback is expected to do something like rb_ary_aref(), rb_struct_aref(),
+ * etc. In case of a range index rb_range_beg_len() expands the range.
+ * Finally return values of the callback are gathered as an array, then
+ * returned.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @param[in] olen "Length" of `obj`.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv List of "indices", described above.
+ * @param[in] func Callback function.
+ * @return A new instance of ::rb_cArray gathering `func`outputs.
+ *
+ * @internal
+ *
+ * `Array#values_at` no longer uses this function. There is no reason apart
+ * from historical ones to list this function here.
+ */
+VALUE rb_get_values_at(VALUE obj, long olen, int argc, const VALUE *argv, VALUE (*func)(VALUE obj, long oidx));
+
+/**
+ * Expands or shrinks the passed array to the passed length.
+ *
+ * @param[out] ary An array to modify.
+ * @param[in] len Desired length of `ary`.
+ * @exception rb_eFrozenError `ary` is frozen.
+ * @exception rb_eIndexError `len` too long.
+ * @return The passed `ary`.
+ * @post `ary`'s length is `len`.
+ * @post Depending on `len` and previous length of `ary` this operation
+ * can also create a series of "hole" positions inside of the
+ * backend storage. They are filled with ::RUBY_Qnil.
+ *
+ * @internal
+ *
+ * `len` is signed. Intentional or...?
+ */
+VALUE rb_ary_resize(VALUE ary, long len);
+
+#define rb_ary_new2 rb_ary_new_capa /**< @old{rb_ary_new_capa} */
+#define rb_ary_new3 rb_ary_new_from_args /**< @old{rb_ary_new_from_args} */
+#define rb_ary_new4 rb_ary_new_from_values /**< @old{rb_ary_new_from_values} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_ARRAY_H */
diff --git a/include/ruby/internal/intern/bignum.h b/include/ruby/internal/intern/bignum.h
new file mode 100644
index 0000000000..c27f77a1fb
--- /dev/null
+++ b/include/ruby/internal/intern/bignum.h
@@ -0,0 +1,846 @@
+#ifndef RBIMPL_INTERN_BIGNUM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_BIGNUM_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to so-called rb_cBignum.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/backward/2/long_long.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* bignum.c */
+
+/**
+ * Allocates a bignum object.
+ *
+ * @param[in] len Length of the bignum's backend storage, in words.
+ * @param[in] sign Sign of the bignum.
+ * @return An allocated new bignum instance.
+ * @note This only allocates an object, doesn't fill its value in.
+ *
+ * @internal
+ *
+ * @shyouhei finds it hard to use from extension libraries. `len` is per
+ * `BDIGIT` but its definition is hidden.
+ */
+VALUE rb_big_new(size_t len, int sign);
+
+/**
+ * Queries if the passed bignum instance is a "bigzero". What is a bigzero?
+ * Well, bignums are for very big integers, but can also represent tiny ones
+ * like -1, 0, 1. Bigzero are instances of bignums whose values are zero.
+ * Knowing if a bignum is bigzero can be handy on occasions, like for instance
+ * detecting division by zero situation.
+ *
+ * @param[in] x A bignum.
+ * @retval 1 It is a bigzero.
+ * @retval 0 Otherwise.
+ */
+int rb_bigzero_p(VALUE x);
+
+/**
+ * Duplicates the given bignum.
+ *
+ * @param[in] num A bignum.
+ * @return An allocated bignum, who is equivalent to `num`.
+ */
+VALUE rb_big_clone(VALUE num);
+
+/**
+ * Destructively modify the passed bignum into 2's complement representation.
+ *
+ * @note By default bignums are in signed magnitude system.
+ *
+ * @param[out] num A bignum to modify.
+ */
+void rb_big_2comp(VALUE num);
+
+/**
+ * Normalises the passed bignum. It for instance returns a fixnum of the same
+ * value if fixnum can represent that number.
+ *
+ * @param[out] x Target bignum (can be destructively modified).
+ * @return An integer of the identical value (can be `x` itself).
+ */
+VALUE rb_big_norm(VALUE x);
+
+/**
+ * Destructively resizes the backend storage of the passed bignum.
+ *
+ * @param[out] big A bignum.
+ * @param[in] len New length of `big`'s backend, in words.
+ */
+void rb_big_resize(VALUE big, size_t len);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Parses C's string to convert into a Ruby's integer. It understands prefixes
+ * (e.g. `0x`) and underscores.
+ *
+ * @param[in] str Stringised representation of the return value.
+ * @param[in] base Base of conversion. Must be `-36..36` inclusive,
+ * except `1`. `2..36` means the conversion is done
+ * according to it, with unmatched prefix understood
+ * as a part of the result. `-36..-2` means the
+ * conversion honours prefix when present, or use
+ * `-base` when absent. `0` is equivalent to `-10`.
+ * `-1` mandates a prefix. `1` is an error.
+ * @param[in] badcheck Whether to raise ::rb_eArgError on failure. If
+ * `0` is passed here this function can return
+ * `INT2FIX(0)` for parse errors.
+ * @exception rb_eArgError Failed to parse (and `badcheck` is truthy).
+ * @return An instance of ::rb_cInteger, which is a numeric interpretation
+ * of what is written in `str`.
+ *
+ * @internal
+ *
+ * Not sure if it intentionally accepts `base == -1` or is just buggy. Nobody
+ * practically uses negative bases these days.
+ */
+VALUE rb_cstr_to_inum(const char *str, int base, int badcheck);
+
+/**
+ * Identical to rb_cstr2inum(), except it takes Ruby's strings instead of C's.
+ *
+ * @param[in] str Stringised representation of the return
+ * value.
+ * @param[in] base Base of conversion. Must be `-36..36`
+ * inclusive, except `1`. `2..36` means the
+ * conversion is done according to it, with
+ * unmatched prefix understood as a part of the
+ * result. `-36..-2` means the conversion
+ * honours prefix when present, or use `-base`
+ * when absent. `0` is equivalent to `-10`.
+ * `-1` mandates a prefix. `1` is an error.
+ * @param[in] badcheck Whether to raise ::rb_eArgError on failure.
+ * If `0` is passed here this function can
+ * return `INT2FIX(0)` for parse errors.
+ * @exception rb_eArgError Failed to parse (and `badcheck` is truthy).
+ * @exception rb_eTypeError `str` is not a string.
+ * @exception rb_eEncCompatError `str` is not ASCII compatible.
+ * @return An instance of ::rb_cInteger, which is a numeric interpretation
+ * of what is written in `str`.
+ */
+VALUE rb_str_to_inum(VALUE str, int base, int badcheck);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_cstr_to_inum(), except the second argument controls the base
+ * and badcheck at once. It basically doesn't raise for parse errors, unless
+ * the base is zero.
+ *
+ * This is an older API. New codes might prefer rb_cstr_to_inum().
+ *
+ * @param[in] str Stringised representation of the return value.
+ * @param[in] base Base of conversion. Must be `-36..36` inclusive,
+ * except `1`. `2..36` means the conversion is done
+ * according to it, with unmatched prefix understood
+ * as a part of the result. `-36..-2` means the
+ * conversion honours prefix when present, or use
+ * `-base` when absent. `0` is equivalent to `-10`.
+ * `-1` mandates a prefix. `1` is an error.
+ * @exception rb_eArgError Failed to parse (and `base` is zero).
+ * @return An instance of ::rb_cInteger, which is a numeric interpretation
+ * of what is written in `str`.
+ */
+VALUE rb_cstr2inum(const char *str, int base);
+
+/**
+ * Identical to rb_str_to_inum(), except the second argument controls the base
+ * and badcheck at once. It can also be seen as a routine identical to
+ * rb_cstr2inum(), except it takes Ruby's strings instead of C's.
+ *
+ * This is an older API. New codes might prefer rb_cstr_to_inum().
+ *
+ * @param[in] str Stringised representation of the return
+ * value.
+ * @param[in] base Base of conversion. Must be `-36..36`
+ * inclusive, except `1`. `2..36` means the
+ * conversion is done according to it, with
+ * unmatched prefix understood as a part of the
+ * result. `-36..-2` means the conversion
+ * honours prefix when present, or use `-base`
+ * when absent. `0` is equivalent to `-10`.
+ * `-1` mandates a prefix. `1` is an error.
+ * @exception rb_eArgError Failed to parse (and `base` is zero).
+ * @exception rb_eTypeError `str` is not a string.
+ * @exception rb_eEncCompatError `str` is not ASCII compatible.
+ * @return An instance of ::rb_cInteger, which is a numeric interpretation
+ * of what is written in `str`.
+ */
+VALUE rb_str2inum(VALUE str, int base);
+
+/**
+ * Generates a place-value representation of the passed integer.
+ *
+ * @param[in] x An integer to stringify.
+ * @param[in] base `2` to `36` inclusive for each radix.
+ * @exception rb_eArgError `base` is out of range.
+ * @exception rb_eRangeError `x` is too big, cannot represent in string.
+ * @return An instance of ::rb_cString which represents `x`.
+ */
+VALUE rb_big2str(VALUE x, int base);
+
+/**
+ * Converts a bignum into C's `long`.
+ *
+ * @param[in] x A bignum.
+ * @exception rb_eRangeError `x` is out of range of `long`.
+ * @return The passed value converted into C's `long`.
+ */
+long rb_big2long(VALUE x);
+
+/** @alias{rb_big2long} */
+#define rb_big2int(x) rb_big2long(x)
+
+/**
+ * Converts a bignum into C's `unsigned long`.
+ *
+ * @param[in] x A bignum.
+ * @exception rb_eRangeError `x` is out of range of `unsigned long`.
+ * @return The passed value converted into C's `unsigned long`.
+ *
+ * @internal
+ *
+ * This function can generate a very large positive integer for a negative
+ * input. For instance applying Ruby's -4,611,686,018,427,387,905 to this
+ * function yields C's 13,835,058,055,282,163,711 on my machine. This is how
+ * it has been. Cannot change any longer.
+ */
+unsigned long rb_big2ulong(VALUE x);
+
+/** @alias{rb_big2long} */
+#define rb_big2uint(x) rb_big2ulong(x)
+
+#if HAVE_LONG_LONG
+/**
+ * Converts a bignum into C's `long long`.
+ *
+ * @param[in] x A bignum.
+ * @exception rb_eRangeError `x` is out of range of `long long`.
+ * @return The passed value converted into C's `long long`.
+ */
+LONG_LONG rb_big2ll(VALUE);
+
+/**
+ * Converts a bignum into C's `unsigned long long`.
+ *
+ * @param[in] x A bignum.
+ * @exception rb_eRangeError `x` is out of range of `unsigned long long`.
+ * @return The passed value converted into C's `unsigned long long`.
+ *
+ * @internal
+ *
+ * This function can generate a very large positive integer for a negative
+ * input. For instance applying Ruby's -4,611,686,018,427,387,905 to this
+ * function yields C's 13,835,058,055,282,163,711 on my machine. This is how
+ * it has been. Cannot change any longer.
+ */
+unsigned LONG_LONG rb_big2ull(VALUE);
+
+#endif /* HAVE_LONG_LONG */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Converts a bignum into a series of its parts.
+ *
+ * @param[in] val An integer.
+ * @param[out] buf Return buffer.
+ * @param[in] num_longs Number of words of `buf`.
+ * @exception rb_eTypeError `val` doesn't respond to `#to_int`.
+ * @post `buf` is filled with `val`'s 2's complement representation, in
+ * the host CPU's native byte order, from least significant word
+ * towards the most significant one, for `num_longs` words.
+ * @note The "pack" terminology comes from `Array#pack`.
+ */
+void rb_big_pack(VALUE val, unsigned long *buf, long num_longs);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Constructs a (possibly very big) bignum from a series of integers. `buf[0]`
+ * would be the return value's least significant word; `buf[num_longs-1]` would
+ * be that of most significant.
+ *
+ * @param[in] buf A series of integers.
+ * @param[in] num_longs Number of words of `buf`.
+ * @exception rb_eArgError Result would be too big.
+ * @return An instance of ::rb_cInteger which is an "unpack"-ed value of
+ * the parameters.
+ * @note The "unpack" terminology comes from `String#pack`.
+ */
+VALUE rb_big_unpack(unsigned long *buf, long num_longs);
+
+/* pack.c */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Encodes a Unicode codepoint into its UTF-8 representation.
+ *
+ * @param[out] buf Return buffer, must at least be 6 bytes width.
+ * @param[in] uv An Unicode codepoint.
+ * @exception rb_eRangeError `uv` is out of Unicode.
+ * @return Number of bytes written to `buf`
+ * @post `buf` holds a UTF-8 representation of `uv`.
+ */
+int rb_uv_to_utf8(char buf[6], unsigned long uv);
+
+/* bignum.c */
+
+/**
+ * Converts a C's `double` into a bignum.
+ *
+ * @param[in] d A value to convert.
+ * @exception rb_eFloatDomainError `d` is Inf/NaN.
+ * @return An instance of ::rb_cInteger whose value is approximately `d`.
+ *
+ * @internal
+ *
+ * @shyouhei is not sure if the result is guaranteed to be the nearest integer
+ * of `d`.
+ */
+VALUE rb_dbl2big(double d);
+
+/**
+ * Converts a bignum into C's `double`.
+ *
+ * @param[in] x A bignum.
+ * @return The passed value converted into C's `double`.
+ *
+ * @internal
+ *
+ * @shyouhei is not sure if the result is guaranteed to be `x`'s nearest value
+ * that a `double` can represent.
+ */
+double rb_big2dbl(VALUE x);
+
+/**
+ * Compares the passed two bignums.
+ *
+ * @param[in] lhs Comparison LHS.
+ * @param[in] rhs Comparison RHS.
+ * @retval -1 `rhs` is bigger than `lhs`.
+ * @retval 0 They are identical.
+ * @retval 1 `lhs` is bigger than `rhs`.
+ * @see rb_num_coerce_cmp()
+ */
+VALUE rb_big_cmp(VALUE lhs, VALUE rhs);
+
+/**
+ * Equality, in terms of `==`. This checks if the _value_ is the same, not the
+ * identity. For instance `1 == 1.0` must hold.
+ *
+ * @param[in] lhs Comparison LHS.
+ * @param[in] rhs Comparison RHS.
+ * @retval RUBY_Qtrue They are the same.
+ * @retval RUBY_Qfalse They are different.
+ */
+VALUE rb_big_eq(VALUE lhs, VALUE rhs);
+
+/**
+ * Equality, in terms of `eql?`. Unlike rb_big_eq() it does not convert
+ * ::rb_cFloat etc. This function returns ::RUBY_Qtrue if and only if both
+ * parameters are bignums, which represent the identical numerical value.
+ *
+ * @param[in] lhs Comparison LHS.
+ * @param[in] rhs Comparison RHS.
+ * @retval RUBY_Qtrue They are identical.
+ * @retval RUBY_Qfalse They are distinct.
+ */
+VALUE rb_big_eql(VALUE lhs, VALUE rhs);
+
+/**
+ * Performs addition of the passed two objects.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x + y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_big_plus(VALUE x, VALUE y);
+
+/**
+ * Performs subtraction of the passed two objects.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x - y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_big_minus(VALUE x, VALUE y);
+
+/**
+ * Performs multiplication of the passed two objects.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x * y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_big_mul(VALUE x, VALUE y);
+
+/**
+ * Performs division of the passed two objects.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x / y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_big_div(VALUE x, VALUE y);
+
+/**
+ * Performs "integer division". This is different from rb_big_div().
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x.div y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_big_idiv(VALUE x, VALUE y);
+
+/**
+ * Performs modulo of the passed two objects.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x % y` evaluates to.
+ * @see rb_num_coerce_bin()
+ *
+ * @internal
+ *
+ * There also is `rb_big_remainder()` internally, which is different from this
+ * one.
+ */
+VALUE rb_big_modulo(VALUE x, VALUE y);
+
+/**
+ * Performs "divmod" operation. The operation in bignum's context is that it
+ * calculates rb_big_idiv() and rb_big_modulo() at once.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x.divmod y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_big_divmod(VALUE x, VALUE y);
+
+/**
+ * Raises `x` to the powerof `y`.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x ** y` evaluates to.
+ * @see rb_num_coerce_bin()
+ * @note This can return an instance of ::rb_cFloat, even when both `x`
+ * and `y` are bignums. Or an instance of ::rb_cRational, when for
+ * instance `y` is negative.
+ */
+VALUE rb_big_pow(VALUE x, VALUE y);
+
+/**
+ * Performs bitwise and of the passed two objects.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x & y` evaluates to.
+ * @see rb_num_coerce_bit()
+ */
+VALUE rb_big_and(VALUE x, VALUE y);
+
+/**
+ * Performs bitwise or of the passed two objects.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x | y` evaluates to.
+ * @see rb_num_coerce_bit()
+ */
+VALUE rb_big_or(VALUE x, VALUE y);
+
+/**
+ * Performs exclusive or of the passed two objects.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x ^ y` evaluates to.
+ * @see rb_num_coerce_bit()
+ */
+VALUE rb_big_xor(VALUE x, VALUE y);
+
+/**
+ * Performs shift left.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Shift amount.
+ * @exception rb_eTypeError `y` is not an integer.
+ * @exception rb_eArgError `y` is too big.
+ * @return `x` shifted left to `y` bits.
+ * @note `y` can be negative. Shifts right then.
+ */
+VALUE rb_big_lshift(VALUE x, VALUE y);
+
+/**
+ * Performs shift right.
+ *
+ * @param[in] x A bignum.
+ * @param[in] y Shift amount.
+ * @exception rb_eTypeError `y` is not an integer.
+ * @return `x` shifted right to `y` bits.
+ * @note This is arithmetic. Because bignums are not bitfields there is
+ * no shift right logical operator.
+ */
+VALUE rb_big_rshift(VALUE x, VALUE y);
+
+/**
+ * @name Flags for rb_integer_pack()/rb_integer_unpack()
+ * @{
+ */
+
+/** Stores/interprets the most significant word as the first word. */
+#define INTEGER_PACK_MSWORD_FIRST 0x01
+
+/** Stores/interprets the least significant word as the first word. */
+#define INTEGER_PACK_LSWORD_FIRST 0x02
+
+/**
+ * Stores/interprets the most significant byte in a word as the first byte in
+ * the word.
+ */
+#define INTEGER_PACK_MSBYTE_FIRST 0x10
+
+/**
+ * Stores/interprets the least significant byte in a word as the first byte in
+ * the word.
+ */
+#define INTEGER_PACK_LSBYTE_FIRST 0x20
+
+/**
+ * Means either #INTEGER_PACK_MSBYTE_FIRST or #INTEGER_PACK_LSBYTE_FIRST,
+ * depending on the host processor's endian.
+ */
+#define INTEGER_PACK_NATIVE_BYTE_ORDER 0x40
+
+/** Uses 2's complement representation. */
+#define INTEGER_PACK_2COMP 0x80
+
+/** Uses "generic" implementation (handy on test). */
+#define INTEGER_PACK_FORCE_GENERIC_IMPLEMENTATION 0x400
+
+/**
+ * Always generates a bignum object even if the integer can be representable
+ * using fixnum scheme (unpack only)
+ */
+#define INTEGER_PACK_FORCE_BIGNUM 0x100
+
+/**
+ * Interprets the input as a signed negative number (unpack only). If not
+ * specified returns a positive number.
+ */
+#define INTEGER_PACK_NEGATIVE 0x200
+
+/** Little endian combination. */
+#define INTEGER_PACK_LITTLE_ENDIAN \
+ (INTEGER_PACK_LSWORD_FIRST | \
+ INTEGER_PACK_LSBYTE_FIRST)
+
+/** Big endian combination */
+#define INTEGER_PACK_BIG_ENDIAN \
+ (INTEGER_PACK_MSWORD_FIRST | \
+ INTEGER_PACK_MSBYTE_FIRST)
+
+/** @} */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Exports an integer into a buffer. This function fills the buffer specified
+ * by `words` and `numwords` as `val` in the format specified by `wordsize`,
+ * `nails` and `flags`.
+ *
+ * @param[in] val Integer or integer-like object which has
+ * `#to_int` method.
+ * @param[out] words Return buffer.
+ * @param[in] numwords Number of words of `words`.
+ * @param[in] wordsize Number of bytes per word.
+ * @param[in] nails Number of padding bits in a word. Most
+ * significant nails bits of each word are filled
+ * by zero.
+ * @param[in] flags Bitwise or of constants whose name starts
+ * "INTEGER_PACK_".
+ * @exception rb_eTypeError `val` doesn't respond to `#to_int`.
+ *
+ * Possible flags are:
+ *
+ * - #INTEGER_PACK_MSWORD_FIRST:
+ * Stores the most significant word as the first word.
+ *
+ * - #INTEGER_PACK_LSWORD_FIRST:
+ * Stores the least significant word as the first word.
+ *
+ * - #INTEGER_PACK_MSBYTE_FIRST:
+ * Stores the most significant byte in a word as the first byte in the
+ * word.
+ *
+ * - #INTEGER_PACK_LSBYTE_FIRST:
+ * Stores the least significant byte in a word as the first byte in the
+ * word.
+ *
+ * - #INTEGER_PACK_NATIVE_BYTE_ORDER:
+ * Either #INTEGER_PACK_MSBYTE_FIRST or #INTEGER_PACK_LSBYTE_FIRST
+ * corresponding to the host's endian.
+ *
+ * - #INTEGER_PACK_2COMP:
+ * Uses 2's complement representation.
+ *
+ * - #INTEGER_PACK_LITTLE_ENDIAN: Shorthand of
+ * `INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_LSBYTE_FIRST`.
+ *
+ * - #INTEGER_PACK_BIG_ENDIAN: Shorthand of
+ * `INTEGER_PACK_MSWORD_FIRST|INTEGER_PACK_MSBYTE_FIRST`.
+ *
+ * - #INTEGER_PACK_FORCE_GENERIC_IMPLEMENTATION:
+ * Uses generic implementation (for test and debug).
+ *
+ * This function fills the buffer specified by `words` as `val`'s 2's
+ * complement representation if #INTEGER_PACK_2COMP is specified in `flags`.
+ * Otherwise it fills `words` as `abs(val)` and signedness is returned via the
+ * return value.
+ *
+ * @return The signedness and overflow condition. The overflow condition
+ * depends on #INTEGER_PACK_2COMP.
+ *
+ * When #INTEGER_PACK_2COMP is not specified:
+ *
+ * - `-2` :
+ * Negative overflow. `val <= -2**(numwords*(wordsize*CHAR_BIT-nails))`
+ *
+ * - `-1` :
+ * Negative without overflow.
+ * `-2**(numwords*(wordsize*CHAR_BIT-nails)) < val < 0`
+ *
+ * - `0` : zero. `val == 0`
+ *
+ * - `1` :
+ * Positive without overflow.
+ * `0 < val < 2**(numwords*(wordsize*CHAR_BIT-nails))`
+ *
+ * - `2` :
+ * Positive overflow. `2**(numwords*(wordsize*CHAR_BIT-nails)) <= val`
+ *
+ * When #INTEGER_PACK_2COMP is specified:
+ *
+ * - `-2` :
+ * Negative overflow. `val < -2**(numwords*(wordsize*CHAR_BIT-nails))`
+ *
+ * - `-1` :
+ * Negative without overflow.
+ * `-2**(numwords*(wordsize*CHAR_BIT-nails)) <= val < 0`
+ *
+ * - `0` : zero. `val == 0`
+ *
+ * - `1` :
+ * Positive without overflow.
+ * `0 < val < 2**(numwords*(wordsize*CHAR_BIT-nails))`
+ *
+ * - `2` :
+ * Positive overflow. `2**(numwords*(wordsize*CHAR_BIT-nails)) <= val`
+ *
+ * The value, `-2**(numwords*(wordsize*CHAR_BIT-nails))`, is representable in
+ * 2's complement representation but not representable in absolute value. So
+ * `-1` is returned for the value if #INTEGER_PACK_2COMP is specified but
+ * returns `-2` if #INTEGER_PACK_2COMP is not specified.
+ *
+ * The least significant words are filled in the buffer when overflow occur.
+ */
+int rb_integer_pack(VALUE val, void *words, size_t numwords, size_t wordsize, size_t nails, int flags);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Import an integer from a buffer.
+ *
+ * @param[in] words Buffer to import.
+ * @param[in] numwords Number of words of `words`.
+ * @param[in] wordsize Number of bytes per word.
+ * @param[in] nails Number of padding bits in a word. Most
+ * significant nails bits of each word are ignored.
+ * @param[in] flags Bitwise or of constants whose name starts
+ * "INTEGER_PACK_".
+ * @exception rb_eArgError `numwords * wordsize` too big.
+ *
+ * Possible flags are:
+ *
+ * - #INTEGER_PACK_MSWORD_FIRST:
+ * Interpret the first word as the most significant word.
+ *
+ * - #INTEGER_PACK_LSWORD_FIRST:
+ * Interpret the first word as the least significant word.
+ *
+ * - #INTEGER_PACK_MSBYTE_FIRST:
+ * Interpret the first byte in a word as the most significant byte in the
+ * word.
+ *
+ * - #INTEGER_PACK_LSBYTE_FIRST:
+ * Interpret the first byte in a word as the least significant byte in
+ * the word.
+ *
+ * - #INTEGER_PACK_NATIVE_BYTE_ORDER:
+ * Either #INTEGER_PACK_MSBYTE_FIRST or #INTEGER_PACK_LSBYTE_FIRST
+ * corresponding to the host's endian.
+ *
+ * - #INTEGER_PACK_2COMP:
+ * Uses 2's complement representation.
+ *
+ * - #INTEGER_PACK_LITTLE_ENDIAN: Shorthand of
+ * `INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_LSBYTE_FIRST`
+ *
+ * - #INTEGER_PACK_BIG_ENDIAN: Shorthand of
+ * `INTEGER_PACK_MSWORD_FIRST|INTEGER_PACK_MSBYTE_FIRST`
+ *
+ * - #INTEGER_PACK_FORCE_BIGNUM:
+ * Returns a bignum even if its value is representable as a fixnum.
+ *
+ * - #INTEGER_PACK_NEGATIVE:
+ * Returns a non-positive value. (Returns a non-negative value if not
+ * specified.)
+ *
+ * - #INTEGER_PACK_FORCE_GENERIC_IMPLEMENTATION:
+ * Uses generic implementation (for test and debug).
+ *
+ * @return An instance of ::rb_cInteger whose value is the interpreted
+ * `words`. The range of the result value depends on
+ * #INTEGER_PACK_2COMP and #INTEGER_PACK_NEGATIVE.
+ *
+ * When #INTEGER_PACK_2COMP is not set:
+ *
+ * - `0 <= val < 2**(numwords*(wordsize*CHAR_BIT-nails))` if
+ * `!INTEGER_PACK_NEGATIVE`
+ *
+ * - `-2**(numwords*(wordsize*CHAR_BIT-nails)) < val <= 0` if
+ * `INTEGER_PACK_NEGATIVE`
+ *
+ * When #INTEGER_PACK_2COMP is set:
+ *
+ * - `-2**(numwords*(wordsize*CHAR_BIT-nails)-1)` `<= val <=`
+ * `2**(numwords*(wordsize*CHAR_BIT-nails)-1)-1` if
+ * `!INTEGER_PACK_NEGATIVE`
+ *
+ * - `-2**(numwords*(wordsize*CHAR_BIT-nails)) <= val <= -1` if
+ * `INTEGER_PACK_NEGATIVE`
+ *
+ * Passing #INTEGER_PACK_2COMP without #INTEGER_PACK_NEGATIVE means sign
+ * extension. #INTEGER_PACK_2COMP with #INTEGER_PACK_NEGATIVE means assuming
+ * the higher bits are `1`.
+ *
+ * Note that this function returns 0 when `numwords` is zero and
+ * #INTEGER_PACK_2COMP is set but #INTEGER_PACK_NEGATIVE is not set.
+ */
+VALUE rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags);
+
+/**
+ * Calculates the number of bytes needed to represent the absolute value of the
+ * passed integer.
+ *
+ * @param[in] val Integer or integer-like object which has
+ * `#to_int` method.
+ * @param[out] nlz_bits_ret Number of leading zero bits in the most
+ * significant byte is returned if not `NULL`.
+ * @exception rb_eTypeError `val` doesn't respond to `#to_int`.
+ * @return `((val_numbits * CHAR_BIT + CHAR_BIT - 1) / CHAR_BIT)`, where
+ * val_numbits is the number of bits of `abs(val)`.
+ * @post If `nlz_bits_ret` is not `NULL`,
+ * `(return_value * CHAR_BIT - val_numbits)` is stored in
+ * `*nlz_bits_ret`. In this case,
+ * `0 <= *nlz_bits_ret < CHAR_BIT`.
+ *
+ * This function should not overflow.
+ */
+size_t rb_absint_size(VALUE val, int *nlz_bits_ret);
+
+/**
+ * Calculates the number of words needed represent the absolute value of the
+ * passed integer. Unlike rb_absint_size() this function can overflow. It
+ * returns `(size_t)-1` then.
+ *
+ * @param[in] val Integer or integer-like object which has
+ * `#to_int` method.
+ * @param[in] word_numbits Number of bits per word.
+ * @param[out] nlz_bits_ret Number of leading zero bits in the most
+ * significant word is returned if not `NULL`.
+ * @exception rb_eTypeError `val` doesn't respond to `#to_int`.
+ * @retval (size_t)-1 Overflowed.
+ * @retval otherwise
+ * `((val_numbits * CHAR_BIT + word_numbits - 1) / word_numbits)`,
+ * where val_numbits is the number of bits of `abs(val)`.
+ * @post If `nlz_bits_ret` is not `NULL` and there is no overflow,
+ * `(return_value * word_numbits - val_numbits)` is stored in
+ * `*nlz_bits_ret`. In this case,
+ * `0 <= *nlz_bits_ret < word_numbits.`
+ *
+ */
+size_t rb_absint_numwords(VALUE val, size_t word_numbits, size_t *nlz_bits_ret);
+
+/**
+ * Tests `abs(val)` consists only of a bit or not.
+ *
+ * @param[in] val Integer or integer-like object which has
+ * `#to_int` method.
+ * @exception rb_eTypeError `val` doesn't respond to `#to_int`.
+ * @retval 1 `abs(val) == 1 << n` for some `n >= 0`.
+ * @retval 0 Otherwise.
+ *
+ * rb_absint_singlebit_p() can be used to determine required buffer size for
+ * rb_integer_pack() used with #INTEGER_PACK_2COMP (two's complement).
+ *
+ * Following example calculates number of bits required to represent val in
+ * two's complement number, without sign bit.
+ *
+ * ```CXX
+ * size_t size;
+ * int neg = FIXNUM_P(val) ? FIX2LONG(val) < 0 : BIGNUM_NEGATIVE_P(val);
+ * size = rb_absint_numwords(val, 1, NULL)
+ * if (size == (size_t)-1) ...overflow...
+ * if (neg && rb_absint_singlebit_p(val))
+ * size--;
+ * ```
+ *
+ * Following example calculates number of bytes required to represent val in
+ * two's complement number, with sign bit.
+ *
+ * ```CXX
+ * size_t size;
+ * int neg = FIXNUM_P(val) ? FIX2LONG(val) < 0 : BIGNUM_NEGATIVE_P(val);
+ * int nlz_bits;
+ * size = rb_absint_size(val, &nlz_bits);
+ * if (nlz_bits == 0 && !(neg && rb_absint_singlebit_p(val)))
+ * size++;
+ * ```
+ */
+int rb_absint_singlebit_p(VALUE val);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_BIGNUM_H */
diff --git a/include/ruby/internal/intern/class.h b/include/ruby/internal/intern/class.h
new file mode 100644
index 0000000000..357af5d176
--- /dev/null
+++ b/include/ruby/internal/intern/class.h
@@ -0,0 +1,394 @@
+#ifndef RBIMPL_INTERN_CLASS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_CLASS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cClass/::rb_cModule.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/backward/2/stdarg.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* class.c */
+
+/**
+ * Creates a new, anonymous class.
+ *
+ * @param[in] super What would become a parent class.
+ * @exception rb_eTypeError `super` is not something inheritable.
+ * @return An anonymous class that inherits `super`.
+ */
+VALUE rb_class_new(VALUE super);
+
+/**
+ * The comment that comes with this function says `:nodoc:`. Not sure what
+ * that means though.
+ *
+ * @param[out] clone Destination object.
+ * @param[in] orig Source object.
+ * @exception rb_eTypeError Cannot copy `orig`.
+ * @return The passed `clone`.
+ */
+VALUE rb_mod_init_copy(VALUE clone, VALUE orig);
+
+/**
+ * Asserts that the given class can derive a child class. A class might or
+ * might not be able to do so; for instance a singleton class cannot.
+ *
+ * @param[in] super Possible super class.
+ * @exception rb_eTypeError No it cannot.
+ * @post Upon successful return `super` can derive.
+ */
+void rb_check_inheritable(VALUE super);
+
+/**
+ * This is a very badly designed API that creates an anonymous class.
+ *
+ * @param[in] id Discarded for no reason (why...).
+ * @param[in] super What would become a parent class. 0 means
+ * ::rb_cObject.
+ * @exception rb_eTypeError `super` is not something inheritable.
+ * @return An anonymous class that inherits `super`.
+ * @warning You must explicitly name the return value.
+ */
+VALUE rb_define_class_id(ID id, VALUE super);
+
+/**
+ * Identical to rb_define_class_under(), except it takes the name in ::ID
+ * instead of C's string.
+ *
+ * @param[out] outer A class which contains the new class.
+ * @param[in] id Name of the new class
+ * @param[in] super A class from which the new class will derive.
+ * 0 means ::rb_cObject.
+ * @exception rb_eTypeError The constant name `id` is already taken but the
+ * constant is not a class.
+ * @exception rb_eTypeError The class is already defined but the class can
+ * not be reopened because its superclass is not
+ * `super`.
+ * @exception rb_eArgError `super` is NULL.
+ * @return The created class.
+ * @post `outer::id` refers the returned class.
+ * @note If a class named `id` is already defined and its superclass is
+ * `super`, the function just returns the defined class.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ */
+VALUE rb_define_class_id_under(VALUE outer, ID id, VALUE super);
+
+/**
+ * Creates a new, anonymous module.
+ *
+ * @return An anonymous module.
+ */
+VALUE rb_module_new(void);
+
+
+/**
+ * Creates a new, anonymous refinement.
+ *
+ * @return An anonymous refinement.
+ */
+VALUE rb_refinement_new(void);
+
+/**
+ * This is a very badly designed API that creates an anonymous module.
+ *
+ * @param[in] id Discarded for no reason (why...).
+ * @return An anonymous module.
+ * @warning You must explicitly name the return value.
+ */
+VALUE rb_define_module_id(ID id);
+
+/**
+ * Identical to rb_define_module_under(), except it takes the name in ::ID
+ * instead of C's string.
+ *
+ * @param[out] outer A class which contains the new module.
+ * @param[in] id Name of the new module
+ * @exception rb_eTypeError The constant name `id` is already taken but the
+ * constant is not a module.
+ * @return The created module.
+ * @post `outer::id` refers the returned module.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ */
+VALUE rb_define_module_id_under(VALUE outer, ID id);
+
+/**
+ * Queries the list of included modules. It can also be seen as a routine to
+ * first call rb_mod_ancestors(), then rejects non-modules from the return
+ * value.
+ *
+ * @param[in] mod Class or Module.
+ * @return An array of modules that are either included or prepended in any
+ * of `mod`'s ancestry tree (including itself).
+ */
+VALUE rb_mod_included_modules(VALUE mod);
+
+/**
+ * Queries if the passed module is included by the module. It can also be seen
+ * as a routine to first call rb_mod_included_modules(), then see if the return
+ * value contains the passed module.
+ *
+ * @param[in] child A Module.
+ * @param[in] parent Another Module.
+ * @exception rb_eTypeError `child` is not an instance of ::rb_cModule.
+ * @retval RUBY_Qtrue `parent` is either included or prepended in any
+ * of `child`'s ancestry tree (including itself).
+ * @return RUBY_Qfalse Otherwise.
+ */
+VALUE rb_mod_include_p(VALUE child, VALUE parent);
+
+/**
+ * Queries the module's ancestors. This routine gathers classes and modules
+ * that the passed module either inherits, includes, or prepends, then
+ * recursively applies that routine again and again to the collected entries
+ * until the list doesn't grow up.
+ *
+ * @param[in] mod A module or a class.
+ * @return An array of classes or modules that `mod` possibly recursively
+ * inherits, includes, or prepends.
+ *
+ * @internal
+ *
+ * Above description is written in a recursive language but in practice it
+ * computes the return value iteratively.
+ */
+VALUE rb_mod_ancestors(VALUE mod);
+
+/**
+ * Queries the class's descendants. This routine gathers classes that are
+ * subclasses of the given class (or subclasses of those subclasses, etc.),
+ * returning an array of classes that have the given class as an ancestor.
+ * The returned array does not include the given class or singleton classes.
+ *
+ * @param[in] klass A class.
+ * @return An array of classes where `klass` is an ancestor.
+ *
+ * @internal
+ */
+VALUE rb_class_descendants(VALUE klass);
+
+/**
+ * Queries the class's direct descendants. This routine gathers classes that are
+ * direct subclasses of the given class,
+ * returning an array of classes that have the given class as a superclass.
+ * The returned array does not include singleton classes.
+ *
+ * @param[in] klass A class.
+ * @return An array of classes where `klass` is the `superclass`.
+ *
+ * @internal
+ */
+VALUE rb_class_subclasses(VALUE klass);
+
+
+/**
+ * Returns the attached object for a singleton class.
+ * If the given class is not a singleton class, raises a TypeError.
+ *
+ * @param[in] klass A class.
+ * @return The object which has the singleton class `klass`.
+ *
+ * @internal
+ */
+VALUE rb_class_attached_object(VALUE klass);
+
+/**
+ * Generates an array of symbols, which are the list of method names defined in
+ * the passed class.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Array of at most one object, which controls (if
+ * any) whether the return array includes the names
+ * of methods defined in ancestors or not.
+ * @param[in] mod A module or a class.
+ * @exception rb_eArgError `argc` out of range.
+ * @return An array of symbols collecting names of instance methods that
+ * are not private, defined at `mod`.
+ */
+VALUE rb_class_instance_methods(int argc, const VALUE *argv, VALUE mod);
+
+/**
+ * Identical to rb_class_instance_methods(), except it returns names of methods
+ * that are public only.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Array of at most one object, which controls (if
+ * any) whether the return array includes the names
+ * of methods defined in ancestors or not.
+ * @param[in] mod A module or a class.
+ * @exception rb_eArgError `argc` out of range.
+ * @return An array of symbols collecting names of instance methods that
+ * are public, defined at `mod`.
+ */
+VALUE rb_class_public_instance_methods(int argc, const VALUE *argv, VALUE mod);
+
+/**
+ * Identical to rb_class_instance_methods(), except it returns names of methods
+ * that are protected only.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Array of at most one object, which controls (if
+ * any) whether the return array includes the names
+ * of methods defined in ancestors or not.
+ * @param[in] mod A module or a class.
+ * @exception rb_eArgError `argc` out of range.
+ * @return An array of symbols collecting names of instance methods that
+ * are protected, defined at `mod`.
+ */
+VALUE rb_class_protected_instance_methods(int argc, const VALUE *argv, VALUE mod);
+
+/**
+ * Identical to rb_class_instance_methods(), except it returns names of methods
+ * that are private only.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Array of at most one object, which controls (if
+ * any) whether the return array includes the names
+ * of methods defined in ancestors or not.
+ * @param[in] mod A module or a class.
+ * @exception rb_eArgError `argc` out of range.
+ * @return An array of symbols collecting names of instance methods that
+ * are protected, defined at `mod`.
+ */
+VALUE rb_class_private_instance_methods(int argc, const VALUE *argv, VALUE mod);
+
+/**
+ * Identical to rb_class_instance_methods(), except it returns names of
+ * singleton methods instead of instance methods.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Array of at most one object, which controls (if
+ * any) whether the return array includes the names
+ * of methods defined in ancestors or not.
+ * @param[in] obj Arbitrary ruby object.
+ * @exception rb_eArgError `argc` out of range.
+ * @return An array of symbols collecting names of instance methods that
+ * are not private, defined at the singleton class of `obj`.
+ */
+VALUE rb_obj_singleton_methods(int argc, const VALUE *argv, VALUE obj);
+
+/**
+ * Identical to rb_define_method(), except it takes the name of the method in
+ * ::ID instead of C's string.
+ *
+ * @param[out] klass A module or a class.
+ * @param[in] mid Name of the function.
+ * @param[in] func The method body.
+ * @param[in] arity The number of parameters. See @ref defmethod.
+ * @note There are in fact 18 different prototypes for func.
+ * @see ::ruby::backward::cxxanyargs::define_method::rb_define_method_id
+ */
+void rb_define_method_id(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int arity);
+
+/* vm_method.c */
+
+/**
+ * Inserts a method entry that hides previous method definition of the given
+ * name. This is not a deletion of a method. Method of the same name defined
+ * in a parent class is kept invisible in this way.
+ *
+ * @param[out] mod The module to insert an undef.
+ * @param[in] mid Name of the undef.
+ * @exception rb_eTypeError `klass` is a non-module.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @exception rb_eNameError No such method named `klass#name`.
+ * @post `klass#name` is undefined.
+ * @see rb_undef_method
+ *
+ * @internal
+ *
+ * @shyouhei doesn't understand why this is not the ::ID -taking variant of
+ * rb_undef_method(), given rb_remove_method() has its ::ID -taking counterpart
+ * named rb_remove_method_id().
+ */
+void rb_undef(VALUE mod, ID mid);
+
+/* class.c */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_define_method(), except it defines a protected method.
+ *
+ * @param[out] klass A module or a class.
+ * @param[in] mid Name of the function.
+ * @param[in] func The method body.
+ * @param[in] arity The number of parameters. See @ref defmethod.
+ * @note There are in fact 18 different prototypes for func.
+ * @see ::ruby::backward::cxxanyargs::define_method::rb_define_protected_method
+ */
+void rb_define_protected_method(VALUE klass, const char *mid, VALUE (*func)(ANYARGS), int arity);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_define_method(), except it defines a private method.
+ *
+ * @param[out] klass A module or a class.
+ * @param[in] mid Name of the function.
+ * @param[in] func The method body.
+ * @param[in] arity The number of parameters. See @ref defmethod.
+ * @note There are in fact 18 different prototypes for func.
+ * @see ::ruby::backward::cxxanyargs::define_method::rb_define_protected_method
+ */
+void rb_define_private_method(VALUE klass, const char *mid, VALUE (*func)(ANYARGS), int arity);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_define_method(), except it defines a singleton method.
+ *
+ * @param[out] obj Arbitrary ruby object.
+ * @param[in] mid Name of the function.
+ * @param[in] func The method body.
+ * @param[in] arity The number of parameters. See @ref defmethod.
+ * @note There are in fact 18 different prototypes for func.
+ * @see ::ruby::backward::cxxanyargs::define_method::rb_define_singleton_method
+ */
+void rb_define_singleton_method(VALUE obj, const char *mid, VALUE(*func)(ANYARGS), int arity);
+
+/**
+ * Finds or creates the singleton class of the passed object.
+ *
+ * @param[out] obj Arbitrary ruby object.
+ * @exception rb_eTypeError `obj` cannot have its singleton class.
+ * @return A (possibly newly allocated) instance of ::rb_cClass.
+ * @post `obj` has its singleton class, which is the return value.
+ * @post In case `obj` is a class, the returned singleton class also has
+ * its own singleton class in order to keep consistency of the
+ * inheritance structure of metaclasses.
+ * @note A new singleton class will be created if `obj` did not have
+ * one.
+ * @note The singleton classes for ::RUBY_Qnil, ::RUBY_Qtrue, and
+ * ::RUBY_Qfalse are ::rb_cNilClass, ::rb_cTrueClass, and
+ * ::rb_cFalseClass respectively.
+ *
+ * @internal
+ *
+ * You can _create_ a singleton class of a frozen object. Intentional or ...?
+ *
+ * Nowadays there are wider range of objects who cannot have singleton classes
+ * than before. For instance some string instances cannot for some reason.
+ */
+VALUE rb_singleton_class(VALUE obj);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_CLASS_H */
diff --git a/include/ruby/internal/intern/compar.h b/include/ruby/internal/intern/compar.h
new file mode 100644
index 0000000000..dc3b377b01
--- /dev/null
+++ b/include/ruby/internal/intern/compar.h
@@ -0,0 +1,62 @@
+#ifndef RBIMPL_INTERN_COMPAR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_COMPAR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_mComparable.
+ */
+#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* bignum.c */
+
+/**
+ * Canonicalises the passed `val`, which is the return value of `a <=> b`, into
+ * C's `{-1, 0, 1}`. This can be handy when you implement a callback function
+ * to pass to `qsort(3)` etc.
+ *
+ * @param[in] val Return value of a space ship operator.
+ * @param[in] a Comparison LHS.
+ * @param[in] b Comparison RHS.
+ * @exception rb_eArgError `a` and `b` are not comparable each other.
+ * @retval -1 `val` is less than zero.
+ * @retval 0 `val` is equal to zero.
+ * @retval 1 `val` is greater than zero.
+ */
+int rb_cmpint(VALUE val, VALUE a, VALUE b);
+
+/* compar.c */
+
+RBIMPL_ATTR_COLD()
+RBIMPL_ATTR_NORETURN()
+/**
+ * Raises "comparison failed" error.
+ *
+ * @param[in] a Comparison LHS.
+ * @param[in] b Comparison RHS.
+ * @exception rb_eArgError `a` and `b` are not comparable each other.
+ */
+void rb_cmperr(VALUE a, VALUE b);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_COMPAR_H */
diff --git a/include/ruby/internal/intern/complex.h b/include/ruby/internal/intern/complex.h
new file mode 100644
index 0000000000..1efc093631
--- /dev/null
+++ b/include/ruby/internal/intern/complex.h
@@ -0,0 +1,249 @@
+#ifndef RBIMPL_INTERN_COMPLEX_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_COMPLEX_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cComplex.
+ */
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/arithmetic/long.h" /* INT2FIX is here. */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* complex.c */
+
+/**
+ * Identical to rb_complex_new(), except it assumes both arguments are not
+ * instances of ::rb_cComplex. It is thus dangerous for extension libraries.
+ *
+ * @param[in] real Real part, in any numeric except Complex.
+ * @param[in] imag Imaginary part, in any numeric except Complex.
+ * @return An instance of ::rb_cComplex whose value is `real + (imag)i`.
+ */
+VALUE rb_complex_raw(VALUE real, VALUE imag);
+
+/**
+ * Shorthand of `x+0i`. It practically converts `x` into a Complex of the
+ * identical value.
+ *
+ * @param[in] x Any numeric except Complex.
+ * @return An instance of ::rb_cComplex, whose value is `x + 0i`.
+ */
+#define rb_complex_raw1(x) rb_complex_raw((x), INT2FIX(0))
+
+/** @alias{rb_complex_raw} */
+#define rb_complex_raw2(x,y) rb_complex_raw((x), (y))
+
+/**
+ * Constructs a Complex, by first multiplying the imaginary part with `1i` then
+ * adds it to the real part. This definition doesn't need both arguments be
+ * real numbers. It can happily combine two instances of ::rb_cComplex (with
+ * rotating the latter one).
+ *
+ * @param[in] real An instance of ::rb_cNumeric.
+ * @param[in] imag Another instance of ::rb_cNumeric.
+ * @return An instance of ::rb_cComplex whose value is `imag * 1i + real`.
+ */
+VALUE rb_complex_new(VALUE real, VALUE imag);
+
+/**
+ * Shorthand of `x+0i`. It practically converts `x` into a Complex of the
+ * identical value.
+ *
+ * @param[in] x Any numeric value.
+ * @return An instance of ::rb_cComplex, whose value is `x + 0i`.
+ */
+#define rb_complex_new1(x) rb_complex_new((x), INT2FIX(0))
+
+/** @alias{rb_complex_new} */
+#define rb_complex_new2(x,y) rb_complex_new((x), (y))
+
+/**
+ * Constructs a Complex using polar representations. Unlike rb_complex_new()
+ * it makes no sense to pass non-real instances to this function.
+ *
+ * @param[in] abs Magnitude, in any numeric except Complex.
+ * @param[in] arg Angle, in radians, in any numeric except Complex.
+ * @return An instance of ::rb_cComplex which denotes the given polar
+ * coordinates.
+ */
+VALUE rb_complex_new_polar(VALUE abs, VALUE arg);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries the real part of the passed Complex.
+ *
+ * @param[in] z An instance of ::rb_cComplex.
+ * @return Its real part, which is an instance of ::rb_cNumeric.
+ */
+VALUE rb_complex_real(VALUE z);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries the imaginary part of the passed Complex.
+ *
+ * @param[in] z An instance of ::rb_cComplex.
+ * @return Its imaginary part, which is an instance of ::rb_cNumeric.
+ */
+VALUE rb_complex_imag(VALUE z);
+
+/**
+ * Performs addition of the passed two objects.
+ *
+ * @param[in] x An instance of ::rb_cComplex.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x + y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_complex_plus(VALUE x, VALUE y);
+
+/**
+ * Performs subtraction of the passed two objects.
+ *
+ * @param[in] x An instance of ::rb_cComplex.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x - y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_complex_minus(VALUE x, VALUE y);
+
+/**
+ * Performs multiplication of the passed two objects.
+ *
+ * @param[in] x An instance of ::rb_cComplex.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x * y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_complex_mul(VALUE x, VALUE y);
+
+/**
+ * Performs division of the passed two objects.
+ *
+ * @param[in] x An instance of ::rb_cComplex.
+ * @param[in] y Arbitrary ruby object.
+ * @return What `x / y` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_complex_div(VALUE x, VALUE y);
+
+/**
+ * Performs negation of the passed object.
+ *
+ * @param[in] z An instance of ::rb_cComplex.
+ * @return What `-z` evaluates to.
+ */
+VALUE rb_complex_uminus(VALUE z);
+
+/**
+ * Performs complex conjugation of the passed object.
+ *
+ * @param[in] z An instance of ::rb_cComplex.
+ * @return Its complex conjugate, in ::rb_cComplex.
+ */
+VALUE rb_complex_conjugate(VALUE z);
+
+/**
+ * Queries the absolute (or the magnitude) of the passed object.
+ *
+ * @param[in] z An instance of ::rb_cComplex.
+ * @return Its magnitude, in ::rb_cFloat.
+ */
+VALUE rb_complex_abs(VALUE z);
+
+/**
+ * Queries the argument (or the angle) of the passed object.
+ *
+ * @param[in] z An instance of ::rb_cComplex.
+ * @return Its magnitude, in ::rb_cFloat.
+ */
+VALUE rb_complex_arg(VALUE z);
+
+/**
+ * Performs exponentiation of the passed two objects.
+ *
+ * @param[in] base An instance of ::rb_cComplex.
+ * @param[in] exp Arbitrary ruby object.
+ * @return What `base ** exp` evaluates to.
+ * @see rb_num_coerce_bin()
+ */
+VALUE rb_complex_pow(VALUE base, VALUE exp);
+
+/**
+ * Identical to rb_complex_new(), except it takes the arguments as C's double
+ * instead of Ruby's object.
+ *
+ * @param[in] real Real part.
+ * @param[in] imag Imaginary part.
+ * @return An instance of ::rb_cComplex whose value is `real + (imag)i`.
+ */
+VALUE rb_dbl_complex_new(double real, double imag);
+
+/** @alias{rb_complex_plus} */
+#define rb_complex_add rb_complex_plus
+
+/** @alias{rb_complex_minus} */
+#define rb_complex_sub rb_complex_minus
+
+/** @alias{rb_complex_uminus} */
+#define rb_complex_nagate rb_complex_uminus
+
+/**
+ * Converts various values into a Complex. This function accepts:
+ *
+ * - Instances of ::rb_cComplex (taken as-is),
+ * - Instances of ::rb_cNumeric (adds `0i`),
+ * - Instances of ::rb_cString (parses),
+ * - Other objects that respond to `#to_c`.
+ *
+ * It (possibly recursively) applies `#to_c` until both sides become a Complex
+ * value, then computes `imag * 1i + real`.
+ *
+ * As a special case, passing ::RUBY_Qundef to `imag` is the same as passing
+ * `RB_INT2NUM(0)`.
+ *
+ * @param[in] real Real part (see above).
+ * @param[in] imag Imaginary part (see above).
+ * @exception rb_eTypeError Passed something not described above.
+ * @return An instance of ::rb_cComplex whose value is `1i * imag + real`.
+ *
+ * @internal
+ *
+ * This was the implementation of `Kernel#Complex` before, but they diverged.
+ */
+VALUE rb_Complex(VALUE real, VALUE imag);
+
+/**
+ * Shorthand of `x+0i`. It practically converts `x` into a Complex of the
+ * identical value.
+ *
+ * @param[in] x ::rb_cNumeric, ::rb_cString, or something that responds to
+ * `#to_c`.
+ * @return An instance of ::rb_cComplex, whose value is `x + 0i`.
+ */
+#define rb_Complex1(x) rb_Complex((x), INT2FIX(0))
+
+/** @alias{rb_Complex} */
+#define rb_Complex2(x,y) rb_Complex((x), (y))
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_COMPLEX_H */
diff --git a/include/ruby/internal/intern/cont.h b/include/ruby/internal/intern/cont.h
new file mode 100644
index 0000000000..2d813ceb9d
--- /dev/null
+++ b/include/ruby/internal/intern/cont.h
@@ -0,0 +1,285 @@
+#ifndef RBIMPL_INTERN_CONT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_CONT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to rb_cFiber.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/iterator.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* cont.c */
+
+/**
+ * Creates a Fiber instance from a C-backended block.
+ *
+ * @param[in] func A function, to become the fiber's body.
+ * @param[in] callback_obj Passed as-is to `func`.
+ * @return An allocated new instance of rb_cFiber, which is ready to be
+ * "resume"d.
+ */
+VALUE rb_fiber_new(rb_block_call_func_t func, VALUE callback_obj);
+
+/**
+ * Creates a Fiber instance from a C-backended block with the specified
+ * storage.
+ *
+ * If the given storage is Qundef or Qtrue, this function is equivalent to
+ * rb_fiber_new() which inherits storage from the current fiber.
+ *
+ * Specifying Qtrue is experimental and may be changed in the future.
+ *
+ * If the given storage is Qnil, this function will lazy initialize the
+ * internal storage which starts of empty (without any inheritance).
+ *
+ * Otherwise, the given storage is used as the internal storage.
+ *
+ * @param[in] func A function, to become the fiber's body.
+ * @param[in] callback_obj Passed as-is to `func`.
+ * @param[in] storage The way to set up the storage for the fiber.
+ * @return An allocated new instance of rb_cFiber, which is ready to be
+ * "resume"d.
+ */
+VALUE rb_fiber_new_storage(rb_block_call_func_t func, VALUE callback_obj, VALUE storage);
+
+/**
+ * Queries the fiber which is calling this function. Any ruby execution
+ * context has its fiber, either explicitly or implicitly.
+ *
+ * @return The current fiber.
+ */
+VALUE rb_fiber_current(void);
+
+/**
+ * Queries the liveness of the passed fiber. "Alive" in this context means
+ * that the fiber can still be resumed. Once it reaches is its end of
+ * execution, this function returns ::RUBY_Qfalse.
+ *
+ * @param[in] fiber A target fiber.
+ * @retval RUBY_Qtrue It is.
+ * @retval RUBY_Qfalse It isn't.
+ */
+VALUE rb_fiber_alive_p(VALUE fiber);
+
+/**
+ * Queries if an object is a fiber.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @retval RUBY_Qtrue It is.
+ * @retval RUBY_Qfalse It isn't.
+ */
+VALUE rb_obj_is_fiber(VALUE obj);
+
+/**
+ * Resumes the execution of the passed fiber, either from the point at which
+ * the last rb_fiber_yield() was called if any, or at the beginning of the
+ * fiber body if it is the first call to this function.
+ *
+ * Other arguments are passed into the fiber's body, either as return values of
+ * rb_fiber_yield() in case it switches to there, or as the block parameter of
+ * the fiber body if it switches to the beginning of the fiber.
+ *
+ * The return value of this function is either the value passed to previous
+ * rb_fiber_yield() call, or the ultimate evaluated value of the entire fiber
+ * body if the execution reaches the end of it.
+ *
+ * When an exception happens inside of a fiber it propagates to this function.
+ *
+ * ```ruby
+ * f = Fiber.new do |i|
+ * puts "<x> =>> #{i}"
+ * puts "<y> <-- #{i + 1}"
+ * j = Fiber.yield(i + 1)
+ * puts "<z> =>> #{j}"
+ * puts "<w> <-- #{j + 1}"
+ * next j + 1
+ * end
+ *
+ * puts "[a] <-- 1"
+ * p = f.resume(1)
+ * puts "[b] =>> #{p}"
+ * puts "[c] <-- #{p + 1}"
+ * q = f.resume(p + 1)
+ * puts "[d] =>> #{q}"
+ * ```
+ *
+ * Above program executes in `[a] <x> <y> [b] [c] <z> <w> [d]`.
+ *
+ * @param[out] fiber The fiber to resume.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed (somehow) to `fiber`.
+ * @exception rb_eFiberError `fib` is terminated etc.
+ * @exception rb_eException Any exceptions happen in `fiber`.
+ * @return (See above)
+ * @note This function _does_ return.
+ *
+ * @internal
+ *
+ * @shyouhei expected this function to raise ::rb_eFrozenError for frozen
+ * fibers but it doesn't in practice. Intentional or ...?
+ */
+VALUE rb_fiber_resume(VALUE fiber, int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_fiber_resume(), except you can specify how to handle the
+ * last element of the given array.
+ *
+ * @param[out] fiber The fiber to resume.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed (somehow) to `fiber`.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eFiberError `fiber` is terminated etc.
+ * @exception rb_eException Any exceptions happen in `fiber`.
+ * @return Either what was yielded or the last value of the fiber body.
+ */
+VALUE rb_fiber_resume_kw(VALUE fiber, int argc, const VALUE *argv, int kw_splat);
+
+/**
+ * Yields the control back to the point where the current fiber was resumed.
+ * The passed objects would be the return value of rb_fiber_resume(). This
+ * fiber then suspends its execution until next time it is resumed.
+ *
+ * This function can also raise arbitrary exceptions injected from outside of
+ * the fiber using rb_fiber_raise().
+ *
+ * ```ruby
+ * exc = Class.new Exception
+ *
+ * f = Fiber.new do
+ * Fiber.yield
+ * rescue exc => e
+ * puts e.message
+ * end
+ *
+ * f.resume
+ * f.raise exc, "Hi!"
+ * ```
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed to rb_fiber_resume().
+ * @exception rb_eException (See above)
+ * @return (See rb_fiber_resume() for details)
+ */
+VALUE rb_fiber_yield(int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_fiber_yield(), except you can specify how to handle the last
+ * element of the given array.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed to rb_fiber_resume().
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eException What was raised using `Fiber#raise`.
+ * @return (See rb_fiber_resume() for details)
+ */
+VALUE rb_fiber_yield_kw(int argc, const VALUE *argv, int kw_splat);
+
+/**
+ * Transfers control to another fiber, resuming it from where it last stopped
+ * or starting it if it was not resumed before. The calling fiber will be
+ * suspended much like in a call to rb_fiber_yield().
+ *
+ * The fiber which receives the transfer call treats it much like a resume
+ * call. Arguments passed to transfer are treated like those passed to resume.
+ *
+ * The two style of control passing to and from fiber (one is rb_fiber_resume()
+ * and rb_fiber_yield(), another is rb_fiber_transfer() to and from fiber)
+ * can't be freely mixed.
+ *
+ * - If the Fiber's lifecycle had started with transfer, it will never be
+ * able to yield or be resumed control passing, only finish or transfer
+ * back. (It still can resume other fibers that are allowed to be
+ * resumed.)
+ *
+ * - If the Fiber's lifecycle had started with resume, it can yield or
+ * transfer to another Fiber, but can receive control back only the way
+ * compatible with the way it was given away: if it had transferred, it
+ * only can be transferred back, and if it had yielded, it only can be
+ * resumed back. After that, it again can transfer or yield.
+ *
+ * If those rules are broken, rb_eFiberError is raised.
+ *
+ * For an individual Fiber design, yield/resume is easier to use (the Fiber
+ * just gives away control, it doesn't need to think about who the control is
+ * given to), while transfer is more flexible for complex cases, allowing to
+ * build arbitrary graphs of Fibers dependent on each other.
+ *
+ * @param[out] fiber Explicit control destination.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed to rb_fiber_resume().
+ * @exception rb_eFiberError (See above)
+ * @exception rb_eException What was raised using `Fiber#raise`.
+ * @return (See rb_fiber_resume() for details)
+ */
+VALUE rb_fiber_transfer(VALUE fiber, int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_fiber_transfer(), except you can specify how to handle the
+ * last element of the given array.
+ *
+ * @param[out] fiber Explicit control destination.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed to rb_fiber_resume().
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eFiberError (See above)
+ * @exception rb_eException What was raised using `Fiber#raise`.
+ * @return (See rb_fiber_resume() for details)
+ */
+VALUE rb_fiber_transfer_kw(VALUE fiber, int argc, const VALUE *argv, int kw_splat);
+
+/**
+ * Identical to rb_fiber_resume() but instead of resuming normal execution of
+ * the passed fiber, it raises the given exception in it. From inside of the
+ * fiber this would be seen as if rb_fiber_yield() raised.
+ *
+ * This function does return in case the passed fiber gracefully handled the
+ * passed exception. But if it does not, the raised exception propagates out
+ * of the passed fiber; this function then does not return.
+ *
+ * Parameters are passed to rb_make_exception() to create an exception object.
+ * See its document for what are allowed here.
+ *
+ * It is a failure to call this function against a fiber which is resuming,
+ * have never run yet, or has already finished running.
+ *
+ * @param[out] fiber Where exception is raised.
+ * @param[in] argc Passed as-is to rb_make_exception().
+ * @param[in] argv Passed as-is to rb_make_exception().
+ * @exception rb_eFiberError `fiber` is terminated etc.
+ * @return (See rb_fiber_resume() for details)
+ */
+VALUE rb_fiber_raise(VALUE fiber, int argc, VALUE *argv);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_CONT_H */
diff --git a/include/ruby/internal/intern/dir.h b/include/ruby/internal/intern/dir.h
new file mode 100644
index 0000000000..da1873e068
--- /dev/null
+++ b/include/ruby/internal/intern/dir.h
@@ -0,0 +1,42 @@
+#ifndef RBIMPL_INTERN_DIR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_DIR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cDir.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* dir.c */
+
+/**
+ * Queries the path of the current working directory of the current process.
+ *
+ * @return An instance of ::rb_cString that holds the working directory.
+ * @note The returned string is in "filesystem" encoding. Most notably on
+ * Linux this is an alias of default external encoding. Most notably
+ * on Windows it can be an alias of OS codepage.
+ */
+VALUE rb_dir_getwd(void);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_DIR_H */
diff --git a/include/ruby/internal/intern/enum.h b/include/ruby/internal/intern/enum.h
new file mode 100644
index 0000000000..215ad82672
--- /dev/null
+++ b/include/ruby/internal/intern/enum.h
@@ -0,0 +1,73 @@
+#ifndef RBIMPL_INTERN_ENUM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_ENUM_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_mEnumerable.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* enum.c */
+
+/**
+ * Basically identical to rb_ary_new_form_values(), except it returns something
+ * different when `argc` < 2.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arbitrary objects.
+ * @retval RUBY_Qnil `argc` is zero.
+ * @retval argv[0] `argc` is one.
+ * @retval otherwise Otherwise.
+ *
+ * @internal
+ *
+ * What is this business? Well, this function is about `yield`'s taking
+ * multiple values. Consider following user-defined class:
+ *
+ * ```ruby
+ * class Foo
+ * include Enumerable
+ *
+ * def each
+ * yield :q, :w, :e, :r
+ * end
+ * end
+ *
+ * Foo.new.each_with_object([]) do |i, j|
+ * j << i # ^^^ <- What to expect for `i`?
+ * end
+ * ```
+ *
+ * Here, `Foo#each_with_object` is in fact `Enumerable#each_with_object`, which
+ * doesn't know what would be yielded. Yet, it has to take a block of arity 2.
+ * This function is used here, to "pack" arbitrary number of yielded objects
+ * into one.
+ *
+ * If people want to implement their own `Enumerable#each_with_object` this API
+ * can be handy. Though @shyouhei suspects it is relatively rare for 3rd party
+ * extension libraries to have such things. Also `Enumerable#each_entry` is
+ * basically this function exposed as a Ruby method.
+ */
+VALUE rb_enum_values_pack(int argc, const VALUE *argv);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_ENUM_H */
diff --git a/include/ruby/internal/intern/enumerator.h b/include/ruby/internal/intern/enumerator.h
new file mode 100644
index 0000000000..00804d786a
--- /dev/null
+++ b/include/ruby/internal/intern/enumerator.h
@@ -0,0 +1,263 @@
+#ifndef RBIMPL_INTERN_ENUMERATOR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_ENUMERATOR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cEnumerator.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/intern/eval.h" /* rb_frame_this_func */
+#include "ruby/internal/iterator.h" /* rb_block_given_p */
+#include "ruby/internal/symbol.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * This is the type of functions that rb_enumeratorize_with_size() expects. In
+ * theory an enumerator can have indefinite number of elements, but in practice
+ * it often is the case we can compute the size of an enumerator beforehand.
+ * If your enumerator has such property, supply a function that calculates such
+ * values.
+ *
+ * @param[in] recv The original receiver of the enumerator.
+ * @param[in] argv Arguments passed to `Object#enum_for` etc.
+ * @param[in] eobj The enumerator object.
+ * @return The size of `eobj`, in ::rb_cNumeric, or ::RUBY_Qnil if the size
+ * is not known until we actually iterate.
+ */
+typedef VALUE rb_enumerator_size_func(VALUE recv, VALUE argv, VALUE eobj);
+
+/**
+ * Decomposed `Enumerator::ArithmeicSequence`. This is a subclass of
+ * ::rb_cEnumerator, which represents a sequence of numbers with common
+ * difference. Internal data structure of the class is opaque to users, but
+ * you can obtain a decomposed one using rb_arithmetic_sequence_extract().
+ */
+typedef struct {
+ VALUE begin; /**< "Left" or "lowest" endpoint of the sequence. */
+ VALUE end; /**< "Right" or "highest" endpoint of the sequence.*/
+ VALUE step; /**< Step between a sequence. */
+ int exclude_end; /**< Whether the endpoint is open or closed. */
+} rb_arithmetic_sequence_components_t;
+
+/* enumerator.c */
+
+/**
+ * Constructs an enumerator. This roughly resembles `Object#enum_for`.
+ *
+ * @param[in] recv A receiver of `meth`.
+ * @param[in] meth Method ID in a symbol object.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to `meth`.
+ * @exception rb_eTypeError `meth` is not an instance of ::rb_cSymbol.
+ * @return A new instance of ::rb_cEnumerator which, when yielded,
+ * enumerates by calling `meth` on `recv` with `argv`.
+ */
+VALUE rb_enumeratorize(VALUE recv, VALUE meth, int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_enumeratorize(), except you can additionally specify the
+ * size function of return value.
+ *
+ * @param[in] recv A receiver of `meth`.
+ * @param[in] meth Method ID in a symbol object.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to `meth`.
+ * @param[in] func Size calculator.
+ * @exception rb_eTypeError `meth` is not an instance of ::rb_cSymbol.
+ * @return A new instance of ::rb_cEnumerator which, when yielded,
+ * enumerates by calling `meth` on `recv` with `argv`.
+ * @note `func` can be zero, which means the size is unknown.
+ */
+VALUE rb_enumeratorize_with_size(VALUE recv, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *func);
+
+/**
+ * Identical to rb_enumeratorize_with_func(), except you can specify how to
+ * handle the last element of the given array.
+ *
+ * @param[in] recv A receiver of `meth`.
+ * @param[in] meth Method ID in a symbol object.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to `meth`.
+ * @param[in] func Size calculator.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eTypeError `meth` is not an instance of ::rb_cSymbol.
+ * @return A new instance of ::rb_cEnumerator which, when yielded,
+ * enumerates by calling `meth` on `recv` with `argv`.
+ * @note `func` can be zero, which means the size is unknown.
+ */
+VALUE rb_enumeratorize_with_size_kw(VALUE recv, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *func, int kw_splat);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Extracts components of the passed arithmetic sequence. This can be seen as
+ * an extended version of rb_range_values().
+ *
+ * @param[in] as Target instance of `Enumerator::ArithmericSequence`.
+ * @param[out] buf Decomposed results buffer.
+ * @return 0 `as` is not `Enumerator::ArithmericSequence`.
+ * @return 1 Success.
+ * @post `buf` is filled.
+ */
+int rb_arithmetic_sequence_extract(VALUE as, rb_arithmetic_sequence_components_t *buf);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_range_beg_len(), except it takes an instance of
+ * `Enumerator::ArithmericSequence`.
+ *
+ * @param[in] as An `Enumerator::ArithmericSequence` instance.
+ * @param[out] begp Return value buffer.
+ * @param[out] lenp Return value buffer.
+ * @param[out] stepp Return value buffer.
+ * @param[in] len Updated length.
+ * @param[in] err In case `len` is out of range...
+ * - `0`: returns ::RUBY_Qnil.
+ * - `1`: raises ::rb_eRangeError.
+ * - `2`: `beg` and `len` expanded accordingly.
+ * @exception rb_eRangeError `as` cannot fit into `long`.
+ * @retval RUBY_Qfalse `as` is not `Enumerator::ArithmericSequence`.
+ * @retval RUBY_Qnil `len` is out of `as` but `err` is zero.
+ * @retval RUBY_Qtrue Otherwise.
+ * @post `beg` is the (possibly updated) left endpoint.
+ * @post `len` is the (possibly updated) length of the range.
+ *
+ * @internal
+ *
+ * Currently no 3rd party applications of this function is found. But that can
+ * be because this function is relatively new.
+ */
+VALUE rb_arithmetic_sequence_beg_len_step(VALUE as, long *begp, long *lenp, long *stepp, long len, int err);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/** @cond INTERNAL_MACRO */
+#ifndef RUBY_EXPORT
+# define rb_enumeratorize_with_size(obj, id, argc, argv, size_fn) \
+ rb_enumeratorize_with_size(obj, id, argc, argv, (rb_enumerator_size_func *)(size_fn))
+# define rb_enumeratorize_with_size_kw(obj, id, argc, argv, size_fn, kw_splat) \
+ rb_enumeratorize_with_size_kw(obj, id, argc, argv, (rb_enumerator_size_func *)(size_fn), kw_splat)
+#endif
+/** @endcond */
+
+/**
+ * This is an implementation detail of #RETURN_SIZED_ENUMERATOR(). You could
+ * use it directly, but can hardly be handy.
+ *
+ * @param[in] obj A receiver.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to the current method.
+ * @param[in] size_fn Size calculator.
+ * @return A new instance of ::rb_cEnumerator which, when yielded,
+ * enumerates by calling the current method on `recv` with `argv`.
+ */
+#define SIZED_ENUMERATOR(obj, argc, argv, size_fn) \
+ rb_enumeratorize_with_size((obj), ID2SYM(rb_frame_this_func()), \
+ (argc), (argv), (size_fn))
+
+/**
+ * This is an implementation detail of #RETURN_SIZED_ENUMERATOR_KW(). You
+ * could use it directly, but can hardly be handy.
+ *
+ * @param[in] obj A receiver.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to the current method.
+ * @param[in] size_fn Size calculator.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @return A new instance of ::rb_cEnumerator which, when yielded,
+ * enumerates by calling the current method on `recv` with `argv`.
+ */
+#define SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat) \
+ rb_enumeratorize_with_size_kw((obj), ID2SYM(rb_frame_this_func()), \
+ (argc), (argv), (size_fn), (kw_splat))
+
+/**
+ * This roughly resembles `return enum_for(__callee__) unless block_given?`.
+ *
+ * @param[in] obj A receiver.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to the current method.
+ * @param[in] size_fn Size calculator.
+ * @note This macro may return inside.
+ */
+#define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) do { \
+ if (!rb_block_given_p()) \
+ return SIZED_ENUMERATOR(obj, argc, argv, size_fn); \
+ } while (0)
+
+
+/**
+ * Identical to #RETURN_SIZED_ENUMERATOR(), except you can specify how to
+ * handle the last element of the given array.
+ *
+ * @param[in] obj A receiver.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to the current method.
+ * @param[in] size_fn Size calculator.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @note This macro may return inside.
+ */
+#define RETURN_SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat) do { \
+ if (!rb_block_given_p()) \
+ return SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat); \
+ } while (0)
+
+/**
+ * Identical to #RETURN_SIZED_ENUMERATOR(), except its size is unknown.
+ *
+ * @param[in] obj A receiver.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to the current method.
+ * @note This macro may return inside.
+ */
+#define RETURN_ENUMERATOR(obj, argc, argv) \
+ RETURN_SIZED_ENUMERATOR(obj, argc, argv, 0)
+
+/**
+ * Identical to #RETURN_SIZED_ENUMERATOR_KW(), except its size is unknown. It
+ * can also be seen as a routine identical to #RETURN_ENUMERATOR(), except you
+ * can specify how to handle the last element of the given array.
+ *
+ * @param[in] obj A receiver.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to the current method.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @note This macro may return inside.
+ */
+#define RETURN_ENUMERATOR_KW(obj, argc, argv, kw_splat) \
+ RETURN_SIZED_ENUMERATOR_KW(obj, argc, argv, 0, kw_splat)
+
+#endif /* RBIMPL_INTERN_ENUMERATOR_H */
diff --git a/include/ruby/internal/intern/error.h b/include/ruby/internal/intern/error.h
new file mode 100644
index 0000000000..1fd9ec2f51
--- /dev/null
+++ b/include/ruby/internal/intern/error.h
@@ -0,0 +1,291 @@
+#ifndef RBIMPL_INTERN_ERROR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_ERROR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_eException.
+ */
+#include "ruby/internal/attr/format.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/backward/2/assume.h"
+
+/**
+ * This macro is used in conjunction with rb_check_arity(). If you pass it to
+ * the function's last (max) argument, that means the function does not check
+ * upper limit.
+ */
+#define UNLIMITED_ARGUMENTS (-1)
+
+#define rb_exc_new2 rb_exc_new_cstr /**< @old{rb_exc_new_cstr} */
+#define rb_exc_new3 rb_exc_new_str /**< @old{rb_exc_new_str} */
+
+/** @cond INTERNAL_MACRO */
+#define rb_check_arity rb_check_arity
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* error.c */
+
+/**
+ * Creates an instance of the passed exception class.
+ *
+ * @param[in] etype A subclass of ::rb_eException.
+ * @param[in] ptr Buffer contains error message.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eTypeError `etype` is not a class.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of `etype`.
+ * @pre At least `len` bytes of continuous memory region shall be
+ * accessible via `ptr`.
+ *
+ * @internal
+ *
+ * This function works for non-exception classes as well, as long as they take
+ * one string argument.
+ */
+VALUE rb_exc_new(VALUE etype, const char *ptr, long len);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_exc_new(), except it assumes the passed pointer is a pointer
+ * to a C string.
+ *
+ * @param[in] etype A subclass of ::rb_eException.
+ * @param[in] str A C string (becomes an error message).
+ * @exception rb_eTypeError `etype` is not a class.
+ * @return An instance of `etype`.
+ */
+VALUE rb_exc_new_cstr(VALUE etype, const char *str);
+
+/**
+ * Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of
+ * C's.
+ *
+ * @param[in] etype A subclass of ::rb_eException.
+ * @param[in] str An instance of ::rb_cString.
+ * @exception rb_eTypeError `etype` is not a class.
+ * @return An instance of `etype`.
+ */
+VALUE rb_exc_new_str(VALUE etype, VALUE str);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+/**
+ * Raises an instance of ::rb_eLoadError.
+ *
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @exception rb_eLoadError Always raises this.
+ * @note It never returns.
+ *
+ * @internal
+ *
+ * Who needs this? Except ruby itself?
+ */
+void rb_loaderror(const char *fmt, ...);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Identical to rb_loaderror(), except it additionally takes which file is
+ * unable to load. The path can be obtained later using `LoadError#path` of
+ * the raising exception.
+ *
+ * @param[in] path What failed.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @exception rb_eLoadError Always raises this.
+ * @note It never returns.
+ */
+void rb_loaderror_with_path(VALUE path, const char *fmt, ...);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Raises an instance of ::rb_eNameError. The name can be obtained later using
+ * `NameError#name` of the raising exception.
+ *
+ * @param[in] name What failed.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @exception rb_eNameError Always raises this.
+ * @note It never returns.
+ */
+void rb_name_error(ID name, const char *fmt, ...);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Identical to rb_name_error(), except it takes a ::VALUE instead of ::ID.
+ *
+ * @param[in] name What failed.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @exception rb_eNameError Always raises this.
+ * @note It never returns.
+ */
+void rb_name_error_str(VALUE name, const char *fmt, ...);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Raises an instance of ::rb_eFrozenError. The object can be obtained later
+ * using `FrozenError#receiver` of the raising exception.
+ *
+ * @param[in] recv What is frozen.
+ * @param[in] fmt Format specifier string compatible with rb_sprintf().
+ * @exception rb_eFrozenError Always raises this.
+ * @note It never returns.
+ *
+ * @internal
+ *
+ * Note however, that it is often not possible to inspect a frozen object,
+ * because the inspection itself could be forbidden by the frozen-ness.
+ */
+void rb_frozen_error_raise(VALUE recv, const char *fmt, ...);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Honestly I don't understand the name, but it raises an instance of
+ * ::rb_eArgError.
+ *
+ * @param[in] str A message.
+ * @param[in] type Another message.
+ * @exception rb_eArgError Always raises this.
+ * @note It never returns.
+ */
+void rb_invalid_str(const char *str, const char *type);
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_frozen_error_raise(), except its raising exception has a
+ * message like "can't modify frozen /what/".
+ *
+ * @param[in] what What was frozen.
+ * @exception rb_eFrozenError Always raises this.
+ * @note It never returns.
+ */
+void rb_error_frozen(const char *what);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_error_frozen(), except it takes arbitrary Ruby object
+ * instead of C's string.
+ *
+ * @param[in] what What was frozen.
+ * @exception rb_eFrozenError Always raises this.
+ * @note It never returns.
+ */
+void rb_error_frozen_object(VALUE what);
+
+/**
+ * Queries if the passed object is frozen.
+ *
+ * @param[in] obj Target object to test frozen-ness.
+ * @exception rb_eFrozenError It is frozen.
+ * @post Upon successful return it is guaranteed _not_ frozen.
+ */
+void rb_check_frozen(VALUE obj);
+
+/**
+ * Ensures that the passed object can be `initialize_copy` relationship. When
+ * you implement your own one you would better call this at the right beginning
+ * of your implementation.
+ *
+ * @param[in] obj Destination object.
+ * @param[in] orig Source object.
+ * @exception rb_eFrozenError `obj` is frozen.
+ * @post Upon successful return obj is guaranteed safe to copy orig.
+ */
+void rb_check_copyable(VALUE obj, VALUE orig);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_scan_args(). You don't have to
+ * bother.
+ *
+ * @pre `argc` is out of range of `min`..`max`, both inclusive.
+ * @param[in] argc Arbitrary integer.
+ * @param[in] min Minimum allowed `argc`.
+ * @param[in] max Maximum allowed `argc`.
+ * @exception rb_eArgError Always.
+ */
+void rb_error_arity(int argc, int min, int max);
+
+void rb_str_modify(VALUE str);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/**
+ * @deprecated
+ *
+ * Does anyone use this? Remain not deleted for compatibility.
+ */
+#define rb_check_frozen_internal rb_check_frozen
+
+/** @alias{rb_check_frozen} */
+static inline void
+rb_check_frozen_inline(VALUE obj)
+{
+ if (RB_UNLIKELY(RB_OBJ_FROZEN(obj))) {
+ rb_error_frozen_object(obj);
+ }
+
+ /* ref: internal CHILLED_STRING_P()
+ This is an implementation detail subject to change. */
+ if (RB_UNLIKELY(RB_TYPE_P(obj, T_STRING) && FL_TEST_RAW(obj, RUBY_FL_USER2 | RUBY_FL_USER3))) { // STR_CHILLED
+ rb_str_modify(obj);
+ }
+}
+
+/* rb_check_frozen() is available as a symbol, but have
+ * the inline version take priority for native consumers. */
+#define rb_check_frozen rb_check_frozen_inline
+
+/**
+ * Ensures that the passed integer is in the passed range. When you can use
+ * rb_scan_args() that is preferred over this one (powerful, descriptive). But
+ * it can have its own application area.
+ *
+ * @param[in] argc Arbitrary integer.
+ * @param[in] min Minimum allowed `argv`.
+ * @param[in] max Maximum allowed `argv`, or `UNLIMITED_ARGUMENTS`.
+ * @exception rb_eArgError `argc` out of range.
+ * @return The passed `argc`.
+ * @post Upon successful return `argc` is in range of `min`..`max`, both
+ * inclusive.
+ */
+static inline int
+rb_check_arity(int argc, int min, int max)
+{
+ if ((argc < min) || (max != UNLIMITED_ARGUMENTS && argc > max))
+ rb_error_arity(argc, min, max);
+ return argc;
+}
+
+#endif /* RBIMPL_INTERN_ERROR_H */
diff --git a/include/ruby/internal/intern/eval.h b/include/ruby/internal/intern/eval.h
new file mode 100644
index 0000000000..2230f7ab0c
--- /dev/null
+++ b/include/ruby/internal/intern/eval.h
@@ -0,0 +1,222 @@
+#ifndef RBIMPL_INTERN_EVAL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_EVAL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Pre-1.9 era evaluator APIs (now considered miscellaneous).
+ */
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* eval.c */
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_raise(), except it raises the passed exception instance as-
+ * is instead of creating new one.
+ *
+ * @param[in] exc An instance of a subclass of ::rb_eException.
+ * @exception exc What is passed.
+ * @exception rb_eTypeError `exc` is not an exception.
+ * @note It never returns.
+ *
+ * @internal
+ *
+ * Wellll actually, it can take more than what is described above. This
+ * function tries to call `exception` method of the passed object. If that
+ * function returns an exception object that is used instead.
+ */
+void rb_exc_raise(VALUE exc);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_fatal(), except it raises the passed exception instance as-
+ * is instead of creating new one.
+ *
+ * @param[in] exc An instance of a subclass of ::rb_eException.
+ * @exception exc What is passed.
+ * @note It never returns.
+ *
+ * @internal
+ *
+ * You know what...? Using this API you can make arbitrary exceptions, like
+ * `RuntimeError`, that doesn't interface with `rescue` clause. This is very
+ * confusing.
+ */
+void rb_exc_fatal(VALUE exc);
+
+/* process.c */
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_exit(), except how arguments are passed.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Contains at most one of the following:
+ * - ::RUBY_Qtrue - means `EXIT_SUCCESS`.
+ * - ::RUBY_Qfalse - means `EXIT_FAILURE`.
+ * - Numerical value - takes that value.
+ * @exception rb_eArgError Wrong `argc`.
+ * @exception rb_eSystemExit Exception representing the exit status.
+ * @note It never returns.
+ */
+VALUE rb_f_exit(int argc, const VALUE *argv);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * This is similar to rb_f_exit(). In fact on some situation it internally
+ * calls rb_exit(). But can be very esoteric on occasions.
+ *
+ * It takes up to one argument. If an argument is passed, it tries to display
+ * that. Otherwise if there is `$!`, displays that exception instead. It
+ * finally raise ::rb_eSystemExit in both cases.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Contains at most one string-ish object.
+ * @exception rb_eArgError Wrong `argc`.
+ * @exception rb_eTypeError No conversion from `argv[0]` to String.
+ * @exception rb_eSystemExit Exception representing `EXIT_FAILURE`.
+ * @note It never returns.
+ */
+VALUE rb_f_abort(int argc, const VALUE *argv);
+
+/* eval.c*/
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Raises an instance of ::rb_eInterrupt.
+ *
+ * @exception rb_eInterrupt Always raises this exception.
+ * @note It never returns.
+ */
+void rb_interrupt(void);
+
+/**
+ * Queries the name of the Ruby level method that is calling this function.
+ * The "name" in this context is the one assigned to the function for the first
+ * time (note that methods can have multiple names via aliases).
+ *
+ * @retval 0 There is no method (e.g. toplevel context).
+ * @retval otherwise The name of the current method.
+ */
+ID rb_frame_this_func(void);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * This function is to re-throw global escapes. Such global escapes include
+ * exceptions, `throw`, `break`, for example.
+ *
+ * It makes sense only when used in conjunction with "protect" series APIs
+ * e.g. rb_protect(), rb_load_protect(), rb_eval_string_protect(), etc. In
+ * case these functions experience global escapes, they fill their opaque
+ * `state` return buffer. You can ignore such escapes. But if you decide
+ * otherwise, you have to somehow escape globally again. This function is used
+ * for that purpose.
+ *
+ * @param[in] state Opaque state of execution.
+ * @note It never returns.
+ *
+ * @internal
+ *
+ * Though not a part of our public API, `state` is in fact an enum
+ * ruby_tag_type. You can see the potential values by looking at vm_core.h.
+ */
+void rb_jump_tag(int state);
+
+/**
+ * Calls `initialize` method of the passed object with the passed arguments.
+ * It also forwards the implicitly passed block to the method.
+ *
+ * @param[in] obj Receiver object.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed as-is to `obj.initialize`.
+ * @exception rb_eException Any exceptions happen inside.
+ */
+void rb_obj_call_init(VALUE obj, int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_obj_call_init(), except you can specify how to handle the
+ * last element of the given array.
+ *
+ * @param[in] obj Receiver object.
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Passed as-is to `obj.initialize`.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eException Any exceptions happen inside.
+ */
+void rb_obj_call_init_kw(VALUE, int, const VALUE*, int);
+
+/**
+ * Identical to rb_frame_this_func(), except it returns the named used to call
+ * the method.
+ *
+ * @retval 0 There is no method (e.g. toplevel context).
+ * @retval otherwise The name of the current method.
+ */
+ID rb_frame_callee(void);
+
+/**
+ * Constructs an exception object from the list of arguments, in a manner
+ * similar to Ruby's `raise`. This function can take:
+ *
+ * - No arguments at all, i.e. `argc == 0`. This is not a failure. It
+ * returns ::RUBY_Qnil then.
+ *
+ * - An object, which is an instance of ::rb_cString. In this case an
+ * instance of ::rb_eRuntimeError whose message is the passed string is
+ * created then returned.
+ *
+ * - An object, which responds to `exception` method, and optionally its
+ * argument, and optionally its backtrace. For example instances of
+ * subclasses of ::rb_eException have this method. What is returned from
+ * the method is returned.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv 0 up to 3 objects.
+ * @exception rb_eArgError Wrong `argc`.
+ * @exception rb_eTypeError `argv[0].exception` returned non-exception.
+ * @return An instance of a subclass of ::rb_eException.
+ *
+ * @internal
+ *
+ * Historically this was _the_ way `raise` converted its arguments to an
+ * exception. However they diverged.
+ */
+VALUE rb_make_exception(int argc, const VALUE *argv);
+
+/* eval_jump.c */
+
+/**
+ * Registers a function that shall run on process exit. Registered functions
+ * run in reverse-chronological order, mixed with syntactic `END` block and
+ * `Kernel#at_exit`.
+ *
+ * @param[in] func Function to run at process exit.
+ * @param[in] arg Passed as-is to `func`.
+ */
+void rb_set_end_proc(void (*func)(VALUE arg), VALUE arg);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_EVAL_H */
diff --git a/include/ruby/internal/intern/file.h b/include/ruby/internal/intern/file.h
new file mode 100644
index 0000000000..8508b7ab9e
--- /dev/null
+++ b/include/ruby/internal/intern/file.h
@@ -0,0 +1,216 @@
+#ifndef RBIMPL_INTERN_FILE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_FILE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cFile.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#if !defined RUBY_EXPORT && !defined RUBY_NO_OLD_COMPATIBILITY
+# include "ruby/backward.h"
+#endif
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* file.c */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_file_expand_path(), except how arguments are passed.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Filename, and base directory, in that order.
+ * @exception rb_eArgError Wrong `argc`.
+ * @exception rb_eTypeError Non-string passed.
+ * @exception rb_eEncCompatError No conversion from arguments to a path.
+ * @return Expanded path.
+ *
+ * @internal
+ *
+ * It seems nobody actually uses this function right now. Maybe delete it?
+ */
+VALUE rb_file_s_expand_path(int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_file_absolute_path(), except it additionally understands
+ * `~`. If a given pathname starts with `~someone/`, that part expands to the
+ * user's home directory (or that of current process' owner's in case of `~/`).
+ *
+ * @param[in] fname Relative file name.
+ * @param[in] dname Lookup base directory name, or in case
+ * ::RUBY_Qnil is passed the process' current
+ * working directory is assumed.
+ * @exception rb_eArgError Home directory is not absolute.
+ * @exception rb_eTypeError Non-string passed.
+ * @exception rb_eEncCompatError No conversion from arguments to a path.
+ * @return Expanded path.
+ */
+VALUE rb_file_expand_path(VALUE fname, VALUE dname);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_file_absolute_path(), except how arguments are passed.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Filename, and base directory, in that order.
+ * @exception rb_eArgError Wrong `argc`.
+ * @exception rb_eTypeError Non-string passed.
+ * @exception rb_eEncCompatError No conversion from arguments to a path.
+ * @return Expanded path.
+ *
+ * @internal
+ *
+ * It seems nobody actually uses this function right now. Maybe delete it?
+ */
+VALUE rb_file_s_absolute_path(int argc, const VALUE *argv);
+
+/**
+ * Maps a relative path to its absolute representation. Relative paths are
+ * referenced from the passed directory name, or from the process' current
+ * working directory in case ::RUBY_Qnil is passed.
+ *
+ * @param[in] fname Relative file name.
+ * @param[in] dname Lookup base directory name, or in case
+ * ::RUBY_Qnil is passed the process' current
+ * working directory is assumed.
+ * @exception rb_eArgError Strings contain NUL bytes.
+ * @exception rb_eTypeError Non-string passed.
+ * @exception rb_eEncCompatError No conversion from arguments to a path.
+ * @return Expanded path.
+ */
+VALUE rb_file_absolute_path(VALUE fname, VALUE dname);
+
+/**
+ * Strips a file path's last component (and trailing separators if any). This
+ * function is relatively simple on POSIX environments; just splits the input
+ * with `/`, strips the last one, if something remains joins them again,
+ * otherwise the return value is `"."`. However when it comes to Windows this
+ * function is quite very much complicated. We have to take UNC etc. into
+ * account. So for instance `"C:foo"`'s dirname is `"C:."`.
+ *
+ * @param[in] fname File name to strip.
+ * @exception rb_eTypeError `fname` is not a String.
+ * @exception rb_eArgError `fname` contains NUL bytes.
+ * @exception rb_eEncCompatError `fname`'s encoding is not path-compat.
+ * @return A dirname of `fname`.
+ * @note This is a "pure" operation; it computes the return value solely
+ * from the passed object and never does any file IO.
+ */
+VALUE rb_file_dirname(VALUE fname);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Resolves a feature's path. This function takes for instance `"json"` and
+ * `[".so", ".rb"]`, and iterates over the `$LOAD_PATH` to see if there is
+ * either `json.so` or `json.rb` in the directory.
+ *
+ * This is not what everything `require` does, but at least `require` is built
+ * on top of it.
+ *
+ * @param[in,out] feature File to search, and return buffer.
+ * @param[in] exts List of file extensions.
+ * @exception rb_eTypeError `feature` is not a String.
+ * @exception rb_eArgError `feature` contains NUL bytes.
+ * @exception rb_eEncCompatError `feature`'s encoding is not path-compat.
+ * @retval 0 Not found
+ * @retval otherwise Found index in `ext`, plus one.
+ * @post `*feature` is a resolved path.
+ */
+int rb_find_file_ext(VALUE *feature, const char *const *exts);
+
+/**
+ * Identical to rb_find_file_ext(), except it takes a feature name and is
+ * extension at once, e.g. `"json.rb"`. This difference is much like how
+ * `require` and `load` are different.
+ *
+ * @param[in] path A path relative to `$LOAD_PATH`.
+ * @exception rb_eTypeError `path` is not a String.
+ * @exception rb_eArgError `path` contains NUL bytes.
+ * @exception rb_eEncCompatError `path`'s encoding is not path-compat.
+ * @return Expanded path.
+ */
+VALUE rb_find_file(VALUE path);
+
+/**
+ * Queries if the given path is either a directory, or a symlink that
+ * (potentially recursively) points to such thing.
+ *
+ * @param[in] _ Ignored (why...?)
+ * @param[in] path String, or IO. In case of IO it issues
+ * `fstat(2)` instead of `stat(2)`.
+ * @exception rb_eFrozenError `path` is a frozen IO (why...?)
+ * @exception rb_eTypeError `path` is neither String nor IO.
+ * @exception rb_eArgError `path` contains NUL bytes.
+ * @exception rb_eEncCompatError `path`'s encoding is not path-compat.
+ * @retval RUBY_Qtrue `path` is a directory.
+ * @retval RUBY_Qfalse Otherwise.
+ */
+VALUE rb_file_directory_p(VALUE _, VALUE path);
+
+/**
+ * Converts a string into an "OS Path" encoding, if any. In most operating
+ * systems there are no such things like per-OS default encoding of filename.
+ * For them this function is no-op. However most notably on MacOS, pathnames
+ * are UTF-8 encoded. It converts the given string into such encoding.
+ *
+ * @param[in] path An instance of ::rb_cString.
+ * @exception rb_eEncCompatError `path`'s encoding is not path-compat.
+ * @return `path`'s contents converted to the OS' path encoding.
+ */
+VALUE rb_str_encode_ospath(VALUE path);
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_PURE()
+/**
+ * Queries if the given path is an absolute path. On POSIX environments it is
+ * as easy as `path[0] == '/'`. However on Windows, drive letters and UNC
+ * paths are also taken into account.
+ *
+ * @param[in] path A possibly relative path string.
+ * @retval 1 `path` is absolute.
+ * @retval 0 `path` is relative.
+ */
+int rb_is_absolute_path(const char *path);
+
+/**
+ * Queries the file size of the given file. Because this function calls
+ * `fstat(2)` internally, it is a failure to pass a closed file to this
+ * function.
+ *
+ * This function flushes the passed file's buffer if any. Can take time.
+ *
+ * @param[in] file A file object.
+ * @exception rb_eFrozenError `file` is frozen.
+ * @exception rb_eIOError `file` is closed.
+ * @exception rb_eSystemCallError Permission denied etc.
+ * @exception rb_eNoMethodError The given non-file object doesn't respond
+ * to `#size`.
+ * @return The size of the passed file.
+ * @note Passing a non-regular file such as a UNIX domain socket to this
+ * function is not a failure. But the return value is
+ * unpredictable. POSIX's `<sys/stat.h>` states that "the use of
+ * this field is unspecified" then.
+ */
+rb_off_t rb_file_size(VALUE file);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_FILE_H */
diff --git a/include/ruby/internal/intern/hash.h b/include/ruby/internal/intern/hash.h
new file mode 100644
index 0000000000..504770fa5f
--- /dev/null
+++ b/include/ruby/internal/intern/hash.h
@@ -0,0 +1,306 @@
+#ifndef RBIMPL_INTERN_HASH_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_HASH_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cHash.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/st.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* hash.c */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_st_foreach(), except it raises exceptions when the callback
+ * function tampers the table during iterating over it.
+ *
+ * @param[in] st Table to iterate over.
+ * @param[in] func Callback function to apply.
+ * @param[in] arg Passed as-is to `func`.
+ * @exception rb_eRuntimeError `st` was tampered during iterating.
+ *
+ * @internal
+ *
+ * This is declared here because exceptions are Ruby level concept.
+ *
+ * This is in fact a very thin wrapper of rb_st_foreach_check().
+ */
+void rb_st_foreach_safe(struct st_table *st, st_foreach_callback_func *func, st_data_t arg);
+
+/** @alias{rb_st_foreach_safe} */
+#define st_foreach_safe rb_st_foreach_safe
+
+/**
+ * Try converting an object to its hash representation using its `to_hash`
+ * method, if any. If there is no such thing, returns ::RUBY_Qnil.
+ *
+ * @param[in] obj Arbitrary ruby object to convert.
+ * @exception rb_eTypeError `obj.to_hash` returned something non-Hash.
+ * @retval RUBY_Qnil No conversion from `obj` to hash defined.
+ * @retval otherwise Converted hash representation of `obj`.
+ * @see rb_io_check_io
+ * @see rb_check_array_type
+ * @see rb_check_string_type
+ *
+ * @internal
+ *
+ * There is no rb_hash_to_hash() that analogous to rb_str_to_str().
+ * Intentional or ...?
+ */
+VALUE rb_check_hash_type(VALUE obj);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Iterates over a hash. This basically does the same thing as
+ * rb_st_foreach(). But because the passed hash is a Ruby object, its keys and
+ * values are both Ruby objects.
+ *
+ * @param[in] hash An instance of ::rb_cHash to iterate over.
+ * @param[in] func Callback function to yield.
+ * @param[in] arg Passed as-is to `func`.
+ * @exception rb_eRuntimeError `hash` was tampered during iterating.
+ */
+void rb_hash_foreach(VALUE hash, int (*func)(VALUE key, VALUE val, VALUE arg), VALUE arg);
+
+/**
+ * Calculates a message authentication code of the passed object. The return
+ * value is a very small integer used as an index of a key of a table. In
+ * order to calculate the value this function calls `#hash` method of the
+ * passed object. Ruby provides you a default implementation. But if you
+ * implement your class in C, that default implementation cannot know the
+ * underlying data structure. You must implement your own `#hash` method then,
+ * which must return an integer of uniform distribution in a sufficiently
+ * instant manner.
+ *
+ * @param[in] obj Arbitrary Ruby object.
+ * @exception rb_eTypeError `obj.hash` returned something non-Integer.
+ * @return A small integer.
+ * @note `#hash` can return very big integers, but they get truncated.
+ */
+VALUE rb_hash(VALUE obj);
+
+/**
+ * Creates a new, empty hash object.
+ *
+ * @return An allocated new instance of ::rb_cHash.
+ */
+VALUE rb_hash_new(void);
+
+/**
+ * Identical to rb_hash_new(), except it additionally specifies how many keys
+ * it is expected to contain. This way you can create a hash that is large enough
+ * for your need. For large hashes it means it won't need to be reallocated and
+ * rehashed as much, improving performance.
+ *
+ * @param[in] capa Designed capacity of the hash.
+ * @return An empty Hash, whose capacity is `capa`.
+ */
+VALUE rb_hash_new_capa(long capa);
+
+/**
+ * Duplicates a hash.
+ *
+ * @param[in] hash An instance of ::rb_cHash.
+ * @return An allocated new instance of ::rb_cHash, whose contents are
+ * a verbatim copy of from `hash`.
+ */
+VALUE rb_hash_dup(VALUE hash);
+
+/** @alias{rb_obj_freeze} */
+VALUE rb_hash_freeze(VALUE obj);
+
+/**
+ * Queries the given key in the given hash table. If there is the key in the
+ * hash, returns the value associated with the key. Otherwise it returns the
+ * "default" value (defined per hash table).
+ *
+ * @param[in] hash Hash table to look into.
+ * @param[in] key Hash key to look for.
+ * @return Either the value associated with the key, or the default one if
+ * absent.
+ */
+VALUE rb_hash_aref(VALUE hash, VALUE key);
+
+/**
+ * Identical to rb_hash_aref(), except it always returns ::RUBY_Qnil for
+ * misshits.
+ *
+ * @param[in] hash Hash table to look into.
+ * @param[in] key Hash key to look for.
+ * @return Either the value associated with the key, or ::RUBY_Qnil if
+ * absent.
+ * @note A hash can store ::RUBY_Qnil as an ordinary value. You cannot
+ * distinguish whether the key is missing, or just its associated
+ * value happens to be ::RUBY_Qnil, as far as you use this API.
+ */
+VALUE rb_hash_lookup(VALUE hash, VALUE key);
+
+/**
+ * Identical to rb_hash_lookup(), except you can specify what to return on
+ * misshits. This is much like 2-arguments version of `Hash#fetch`.
+ *
+ * ```CXX
+ * VALUE hash;
+ * VALUE key;
+ * VALUE tmp = rb_obj_alloc(rb_cObject);
+ * VALUE val = rb_hash_lookup2(hash, key, tmp);
+ * if (val == tmp) {
+ * printf("misshit");
+ * }
+ * else {
+ * printf("hit");
+ * }
+ * ```
+ *
+ * @param[in] hash Hash table to look into.
+ * @param[in] key Hash key to look for.
+ * @param[in] def Default value.
+ * @retval def `hash` does not have `key`.
+ * @retval otherwise The value associated with `key`.
+ */
+VALUE rb_hash_lookup2(VALUE hash, VALUE key, VALUE def);
+
+/**
+ * Identical to rb_hash_lookup(), except it yields the (implicitly) passed
+ * block instead of returning ::RUBY_Qnil.
+ *
+ * @param[in] hash Hash table to look into.
+ * @param[in] key Hash key to look for.
+ * @exception rb_eKeyError No block given.
+ * @return Either the value associated with the key, or what the block
+ * evaluates to if absent.
+ */
+VALUE rb_hash_fetch(VALUE hash, VALUE key);
+
+/**
+ * Inserts or replaces ("upsert"s) the objects into the given hash table. This
+ * basically associates the given value with the given key. On duplicate key
+ * this function updates its associated value with the given one. Otherwise it
+ * inserts the association at the end of the table.
+ *
+ * @param[out] hash Target hash table to modify.
+ * @param[in] key Arbitrary Ruby object.
+ * @param[in] val A value to be associated with `key`.
+ * @exception rb_eFrozenError `hash` is frozen.
+ * @return The passed `val`
+ * @post `val` is associated with `key` in `hash`.
+ */
+VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val);
+
+/**
+ * Swipes everything out of the passed hash table.
+ *
+ * @param[out] hash Target to clear.
+ * @exception rb_eFrozenError `hash`is frozen.
+ * @return The passed `hash`
+ * @post `hash` has no contents.
+ */
+VALUE rb_hash_clear(VALUE hash);
+
+/**
+ * Deletes each entry for which the block returns a truthy value. If there is
+ * no block given, it returns an enumerator that does the thing.
+ *
+ * @param[out] hash Target hash to modify.
+ * @exception rb_eFrozenError `hash` is frozen.
+ * @retval hash The hash is modified.
+ * @retval otherwise An instance of ::rb_cEnumerator that does it.
+ */
+VALUE rb_hash_delete_if(VALUE hash);
+
+/**
+ * Deletes the passed key from the passed hash table, if any.
+ *
+ * @param[out] hash Target hash to modify.
+ * @param[in] key Key to delete.
+ * @retval RUBY_Qnil `hash` has no such key as `key`.
+ * @retval otherwise What was associated with `key`.
+ * @post `hash` has no such key as `key`.
+ */
+VALUE rb_hash_delete(VALUE hash, VALUE key);
+
+/**
+ * Inserts a list of key-value pairs into a hash table at once. It is
+ * semantically identical to repeatedly calling rb_hash_aset(), but can be
+ * faster than that.
+ *
+ * @param[in] argc Length of `argv`, must be even.
+ * @param[in] argv A list of key, value, key, value, ...
+ * @param[out] hash Target hash table to modify.
+ * @post `hash` has contents from `argv`.
+ * @note `argv` is allowed to be NULL as long as `argc` is zero.
+ *
+ * @internal
+ *
+ * What happens for duplicated keys? Well it silently discards older ones to
+ * accept the newest (rightmost) one. This behaviour also mimics repeated call
+ * of rb_hash_aset().
+ */
+void rb_hash_bulk_insert(long argc, const VALUE *argv, VALUE hash);
+
+/**
+ * Type of callback functions to pass to rb_hash_update_by().
+ *
+ * @param[in] newkey A key of the table.
+ * @param[in] oldkey Value associated with `key` in hash1.
+ * @param[in] value Value associated with `key` in hash2.
+ * @return Either one of the passed values to take.
+ */
+typedef VALUE rb_hash_update_func(VALUE newkey, VALUE oldkey, VALUE value);
+
+/**
+ * Destructively merges two hash tables into one. It resolves key conflicts by
+ * calling the passed function and take its return value.
+ *
+ * @param[out] hash1 Target hash to be modified.
+ * @param[in] hash2 A hash to merge into `hash1`.
+ * @param[in] func Conflict reconciler.
+ * @exception rb_eFrozenError `hash1` is frozen.
+ * @exception rb_eRuntimeError `hash2` is updated instead.
+ * @return The passed `hash1`.
+ * @post Contents of `hash2` is merged into `hash1`.
+ * @note You can pass zero to `func`. This means values from `hash2`
+ * are always taken.
+ */
+VALUE rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func);
+
+/**
+ * Destructively removes every environment variables of the running process.
+ *
+ * @return The `ENV` object.
+ * @post The process has no environment variables.
+ */
+VALUE rb_env_clear(void);
+
+/**
+ * Identical to #RHASH_SIZE(), except it returns the size in Ruby's integer
+ * instead of C's.
+ *
+ * @param[in] hash A hash object.
+ * @return The size of the hash.
+ */
+VALUE rb_hash_size(VALUE hash);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_HASH_H */
diff --git a/include/ruby/internal/intern/io.h b/include/ruby/internal/intern/io.h
new file mode 100644
index 0000000000..b9eb258cc1
--- /dev/null
+++ b/include/ruby/internal/intern/io.h
@@ -0,0 +1,661 @@
+#ifndef RBIMPL_INTERN_IO_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_IO_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cIO.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* io.c */
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define rb_defout rb_stdout
+
+/* string.c */ /* ...why? moved in commit de7161526014b781468cea5d84411e23be */
+
+/**
+ * The field separator character for inputs, or the `$;`. This affects how
+ * `String#split` works. You can set this via the `-F` command line option.
+ * You can also assign arbitrary ruby objects programmatically, but it makes
+ * best sense for you to assign a regular expression here.
+ *
+ * @internal
+ *
+ * Tidbit: "fs" comes from AWK's `FS` variable.
+ */
+RUBY_EXTERN VALUE rb_fs;
+
+/* io.c */ /* ...why? given rb_fs is in string.c? */
+
+/**
+ * The field separator character for outputs, or the `$,`. This affects how
+ * `Array#join` works.
+ *
+ * @deprecated Assigning anything other than ::RUBY_Qnil to this variable is
+ * deprecated.
+ */
+RUBY_EXTERN VALUE rb_output_fs;
+
+/**
+ * The record separator character for inputs, or the `$/`. This affects how
+ * `IO#gets` works. You can set this via the `-0` command line option.
+ *
+ * @deprecated Assigning anything other than ::RUBY_Qnil to this variable is
+ * deprecated.
+ *
+ * @internal
+ *
+ * Tidbit: "rs" comes from AWK's `RS` variable.
+ */
+RUBY_EXTERN VALUE rb_rs;
+
+/**
+ * This is the default value of ::rb_rs, i.e. `"\n"`. It seems it has always
+ * been just a newline string since the beginning. Not sure why C codes has to
+ * use this, given there is no way for ruby programs to interface.
+ *
+ * Also it has not been deprecated for unknown reasons.
+ */
+RUBY_EXTERN VALUE rb_default_rs;
+
+/**
+ * The record separator character for outputs, or the `$\`. This affects how
+ * `IO#print` works.
+ *
+ * @deprecated Assigning anything other than ::RUBY_Qnil to this variable is
+ * deprecated.
+ */
+RUBY_EXTERN VALUE rb_output_rs;
+
+/**
+ * Writes the given string to the given IO.
+ *
+ * @param[out] io An IO, opened for writing.
+ * @param[in] str A String-like object to write to `io`.
+ * @exception rb_eIOError `io` isn't opened for writing.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eTypeError No conversion from `str` to String.
+ * @exception rb_eSystemCallError `write(2)` failed for some reason.
+ * @return The number of bytes written to the `io`.
+ * @post `str` (up to the length of return value) is written to `io`.
+ * @note This function blocks.
+ * @note Partial write is a thing. It must be at least questionable not
+ * to check the return value.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This function can take arbitrary
+ * objects, and calls their `write` method. What is written above in fact
+ * describes how `IO#write` works. You can pass StringIO etc. here, and would
+ * work completely differently.
+ */
+VALUE rb_io_write(VALUE io, VALUE str);
+
+/**
+ * Reads a "line" from the given IO. A line here means a chunk of characters
+ * which is terminated by either `"\n"` or an EOF.
+ *
+ * @param[in,out] io An IO, opened for reading.
+ * @exception rb_eIOError `io` isn't opened for reading.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @retval RUBY_Qnil `io` is at EOF.
+ * @retval otherwise An instance of ::rb_cString.
+ * @post `io` is read.
+ * @note Unlike `IO#gets` it doesn't set `$_`.
+ * @note Unlike `IO#gets` it doesn't consider `$/`.
+ */
+VALUE rb_io_gets(VALUE io);
+
+/**
+ * Reads a byte from the given IO.
+ *
+ * @note In Ruby a "byte" always means an 8 bit integer ranging from
+ * 0 to 255 inclusive.
+ * @param[in,out] io An IO, opened for reading.
+ * @exception rb_eIOError `io` is not opened for reading.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @retval RUBY_Qnil `io` is at EOF.
+ * @retval otherwise An instance of ::rb_cInteger.
+ * @post `io` is read.
+ *
+ * @internal
+ *
+ * Of course there was a function called `rb_io_getc()`. It was removed in
+ * commit a25fbe3b3e531bbe479f344af24eaf9d2eeae6ea.
+ */
+VALUE rb_io_getbyte(VALUE io);
+
+/**
+ * "Unget"s a string. This function pushes back the passed string onto the
+ * passed IO, such that a subsequent buffered read will return it. If the
+ * passed content is in fact an integer, a single character string of that
+ * codepoint of the encoding of the IO will be pushed back instead.
+ *
+ * It might be counter-intuitive but this function can push back multiple
+ * characters at once. Also this function can be called multiple times on a
+ * same IO. Also a "character" can be wider than a byte, depending on the
+ * encoding of the IO.
+ *
+ * @param[out] io An IO, opened for reading.
+ * @param[in] c Either a String, or an Integer.
+ * @exception rb_eIOError `io` is not opened for reading.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eTypeError No conversion from `c` to ::rb_cString.
+ * @return Always returns ::RUBY_Qnil.
+ *
+ * @internal
+ *
+ * Why there is ungetc, given there is no getc?
+ */
+VALUE rb_io_ungetc(VALUE io, VALUE c);
+
+/**
+ * Identical to rb_io_ungetc(), except it doesn't take the encoding of the
+ * passed IO into account. When an integer is passed, it just casts that value
+ * to C's `unsigned char`, and pushes that back.
+ *
+ * @param[out] io An IO, opened for reading.
+ * @param[in] b Either a String, or an Integer.
+ * @exception rb_eIOError `io` is not opened for reading.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eTypeError No conversion from `b` to ::rb_cString.
+ * @return Always returns ::RUBY_Qnil.
+ */
+VALUE rb_io_ungetbyte(VALUE io, VALUE b);
+
+/**
+ * Closes the IO. Any buffered contents are flushed to the operating system.
+ * Any future operations against the IO would raise ::rb_eIOError. In case the
+ * io was created using `IO.popen`, it also sets the `$?`.
+ *
+ * @param[out] io Target IO to close.
+ * @return Always returns ::RUBY_Qnil.
+ * @post `$?` is set in case IO is a pipe.
+ * @post No operations are possible against `io` any further.
+ * @note This can block to flush the contents.
+ * @note This can wake other threads up, especially those who are
+ * `select()`-ing the passed IO.
+ * @note Multiple invocations of this function over the same IO again
+ * and again is not an error, since Ruby 2.3.
+ *
+ * @internal
+ *
+ * You can close a frozen IO... Is this intentional?
+ */
+VALUE rb_io_close(VALUE io);
+
+/**
+ * Flushes any buffered data within the passed IO to the underlying operating
+ * system.
+ *
+ * @param[out] io Target IO to flush.
+ * @exception rb_eIOError `io` is closed.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eSystemCallError `write(2)` failed for some reason.
+ * @return The passed `io`.
+ * @post `io`'s buffers are empty.
+ * @note This operation also discards the read buffer. Should basically
+ * be harmless, but in an esoteric situation like when user pushed
+ * something different from what was read using `ungetc`, this
+ * operation in fact changes the behaviour of the `io`.
+ * @note Buffering is difficult. This operation flushes the data from
+ * our userspace to the kernel, but that doesn't always mean you
+ * can expect them stored persistently onto your hard drive.
+ */
+VALUE rb_io_flush(VALUE io);
+
+/**
+ * Queries if the passed IO is at the end of file. "The end of file" here mans
+ * that there are no more data to read. This function blocks until the read
+ * buffer is filled in, and if that operation reached the end of file, it still
+ * returns ::RUBY_Qfalse (because there are data yet in that buffer). It
+ * returns ::RUBY_Qtrue once after the buffer is cleared.
+ *
+ * @param[in,out] io Target io to query.
+ * @exception rb_eIOError `io` is not opened for reading.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @retval RUBY_Qfalse There are things yet to be read.
+ * @retval RUBY_Qtrue "The end of file" situation.
+ */
+VALUE rb_io_eof(VALUE io);
+
+/**
+ * Sets the binmode. This operation nullifies the effect of textmode (newline
+ * conversion from `"\r\n"` to `"\n"` or vice versa). Note that it doesn't
+ * stop character encodings conversions. For instance an IO created using:
+ *
+ * ```ruby
+ * File.open(
+ * "/dev/urandom",
+ * textmode: true,
+ * external_encoding: Encoding::GB18030,
+ * internal_encoding: Encoding::Windows_31J)
+ * ```
+ *
+ * has both newline and character conversions. If you pass such IO to this
+ * function, only the `textmode:true` part is cancelled. Texts read through
+ * the IO would still be encoded in Windows-31J; texts written to the IO will
+ * be encoded in GB18030.
+ *
+ * @param[out] io Target IO to modify.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @return The passed `io`.
+ * @post `io` is in binmode.
+ * @note There is no equivalent operation in Ruby. You can do this only
+ * in C.
+ */
+VALUE rb_io_binmode(VALUE io);
+
+/**
+ * Forces no conversions be applied to the passed IO. Unlike rb_io_binmode(),
+ * this cancels any newline conversions as well as encoding conversions. Any
+ * texts read/written through the IO will be the verbatim binary contents.
+ *
+ * @param[out] io Target IO to modify.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @return The passed `io`.
+ * @post `io` is in binmode. Both external/internal encoding are set to
+ * rb_ascii8bit_encoding().
+ * @note This is the implementation of `IO#binmode`.
+ */
+VALUE rb_io_ascii8bit_binmode(VALUE io);
+
+/**
+ * Identical to rb_io_write(), except it always returns the passed IO.
+ *
+ * @param[out] io An IO, opened for writing.
+ * @param[in] str A String-like object to write to `io`.
+ * @exception rb_eIOError `io` isn't opened for writing.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eTypeError No conversion from `str` to String.
+ * @exception rb_eSystemCallError `write(2)` failed.
+ * @return The passed `io`.
+ * @post `str` is written to `io`.
+ * @note This function blocks.
+ *
+ * @internal
+ *
+ * As rb_io_write(), above description is a fake.
+ */
+VALUE rb_io_addstr(VALUE io, VALUE str);
+
+/**
+ * This is a rb_f_sprintf() + rb_io_write() combo.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv A format string followed by its arguments.
+ * @param[out] io An IO, opened for writing.
+ * @exception rb_eIOError `io` isn't opened for writing.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eTypeError No conversion from `str` to String.
+ * @exception rb_eSystemCallError `write(2)` failed.
+ * @return Always returns ::RUBY_Qnil.
+ * @post `argv` is formatted, then written to `io`.
+ * @note This function blocks.
+ *
+ * @internal
+ *
+ * As rb_io_write(), above descriptions include fakes.
+ */
+VALUE rb_io_printf(int argc, const VALUE *argv, VALUE io);
+
+/**
+ * Iterates over the passed array to apply rb_io_write() individually. If
+ * there is `$,`, this function inserts the string in middle of each
+ * iterations. If there is `$\`, this function appends the string at the end.
+ * If the array is empty, this function outputs `$_`.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv An array of strings to display.
+ * @param[out] io An IO, opened for writing.
+ * @exception rb_eIOError `io` isn't opened for writing.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eTypeError No conversion from `str` to String.
+ * @exception rb_eSystemCallError `write(2)` failed.
+ * @return Always returns ::RUBY_Qnil.
+ * @post `argv` is written to `io`.
+ * @note This function blocks.
+ * @note This function calls rb_io_write() multiple times. Which means,
+ * it is not an atomic operation. Outputs from multiple threads
+ * can interleave.
+ *
+ * @internal
+ *
+ * As rb_io_write(), above descriptions include fakes.
+ */
+VALUE rb_io_print(int argc, const VALUE *argv, VALUE io);
+
+/**
+ * Iterates over the passed array to apply rb_io_write() individually. Unlike
+ * rb_io_print(), this function prints a newline per each element. It also
+ * flattens the passed array (OTOH rb_io_print() just resorts to
+ * rb_ary_to_s()).
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv An array of strings to display.
+ * @param[out] io An IO, opened for writing.
+ * @exception rb_eIOError `io` isn't opened for writing.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eTypeError No conversion from `str` to String.
+ * @exception rb_eSystemCallError `write(2)` failed.
+ * @return Always returns ::RUBY_Qnil.
+ * @post `argv` is written to `io`.
+ * @note This function blocks.
+ * @note This function calls rb_io_write() multiple times. Which means,
+ * it is not an atomic operation. Outputs from multiple threads
+ * can interleave.
+ *
+ * @internal
+ *
+ * As rb_io_write(), above descriptions include fakes.
+ */
+VALUE rb_io_puts(int argc, const VALUE *argv, VALUE io);
+
+/**
+ * Creates an IO instance whose backend is the given file descriptor. C
+ * extension libraries sometimes have file descriptors created elsewhere (maybe
+ * deep inside of another shared library), which they want ruby programs to
+ * handle. This function is handy for such situations.
+ *
+ * @param[in] fd Target file descriptor.
+ * @param[in] flags Flags, e.g. `O_CREAT|O_EXCL`
+ * @param[in] path The path of the file that backs `fd`, for diagnostics.
+ * @return An allocated instance of ::rb_cIO with the autoclose flag set.
+ * @note Leave `path` NULL if you don't know.
+ */
+VALUE rb_io_fdopen(int fd, int flags, const char *path);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Opens a file located at the given path.
+ *
+ * `fmode` is a C string that represents the open mode. It can be one of:
+ *
+ * - `r` (means `O_RDONLY`),
+ * - `w` (means `O_WRONLY | O_TRUNC | O_CREAT`),
+ * - `a` (means `O_WRONLY | O_APPEND | O_CREAT`),
+ *
+ * Followed by zero or more combinations of:
+ *
+ * - `b` (means `_O_BINARY`),
+ * - `t` (means `_O_TEXT`),
+ * - `+` (means `O_RDWR`),
+ * - `x` (means `O_TRUNC`), or
+ * - `:[BOM|]enc[:enc]` (see below).
+ *
+ * This last one specifies external (and internal if any) encodings,
+ * respectively. If optional `BOM|` is specified and the specified external
+ * encoding is capable of expressing BOMs, opening file's contents' byte order
+ * is auto-detected using the mechanism.
+ *
+ * So for instance, fmode of `"rt|BOM:utf-16le:utf-8"` specifies that...
+ *
+ * - the physical representation of the contents of the file is in UTF-16;
+ * - honours its BOM but assumes little endian if absent;
+ * - opens the file for reading;
+ * - what is read is converted into UTF-8;
+ * - with newlines cannibalised to `\n`.
+ *
+ * @param[in] fname Path to open.
+ * @param[in] fmode Mode specifier much like `fopen(3)`.
+ * @exception rb_eArgError `fmode` contradicted (e.g. `"bt"`).
+ * @exception rb_eSystemCallError `open(2)` failed for some reason.
+ * @return An instance of ::rb_cIO.
+ */
+VALUE rb_file_open(const char *fname, const char *fmode);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_file_open(), except it takes the pathname as a Ruby's string
+ * instead of C's. In case the passed Ruby object is a non-String it tries to
+ * call `#to_path`.
+ *
+ * @param[in] fname Path to open.
+ * @param[in] fmode Mode specifier much like `fopen(3)`.
+ * @exception rb_eTypeError `fname` is not a String.
+ * @exception rb_eEncCompatError `fname` is not ASCII-compatible.
+ * @exception rb_eArgError `fmode` contradicted (e.g. `"bt"`).
+ * @exception rb_eSystemCallError `open(2)` failed for some reason.
+ * @return An instance of ::rb_cIO.
+ */
+VALUE rb_file_open_str(VALUE fname, const char *fmode);
+
+/**
+ * Much like rb_io_gets(), but it reads from the mysterious ARGF object. ARGF
+ * in this context can be seen as a virtual IO which concatenates contents of
+ * the files passed to the process via the ARGV, or just STDIN if there are no
+ * such files.
+ *
+ * Unlike rb_io_gets() this function sets `$_`.
+ *
+ * @exception rb_eFrozenError ARGF resorts to STDIN but it is frozen.
+ * @retval RUBY_Qnil ARGF is at EOF.
+ * @retval otherwise An instance of ::rb_cString.
+ * @post ARGF is read.
+ * @post `$_` is set.
+ *
+ * @internal
+ *
+ * In reality, this function can call `ARGF.gets`. Its redefinition can affect
+ * the behaviour.
+ *
+ * Also, you can tamper ARGV on-the-fly in middle of ARGF usages:
+ *
+ * ```
+ * gets # Reads the first file.
+ * ARGV << '/proc/self/limits' # Adds a file.
+ * gets # Can read from /proc/self/limits.
+ * ```
+ */
+VALUE rb_gets(void);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Writes the given error message to somewhere applicable. On Windows it goes
+ * to the console. On POSIX environments it goes to the standard error.
+ *
+ * @warning IT IS A BAD IDEA to use this function form your C extensions.
+ * It is often annoying when GUI applications write to consoles;
+ * users don't want to look at there. Programmers also want to
+ * control the cause of the message itself, like by rescuing an
+ * exception. Just let ruby handle errors. That must be better than
+ * going your own way.
+ *
+ * @param[in] str Error message to display.
+ * @post `str` is written to somewhere.
+ *
+ * @internal
+ *
+ * AFAIK this function is listed here without marked deprecated because there
+ * are usages of this function in the wild.
+ */
+void rb_write_error(const char *str);
+
+/**
+ * Identical to rb_write_error(), except it additionally takes the message's
+ * length. Necessary when you want to handle wide characters.
+ *
+ * @param[in] str Error message to display.
+ * @param[in] len Length of `str`, in bytes.
+ * @post `str` is written to somewhere.
+ */
+void rb_write_error2(const char *str, long len);
+
+/**
+ * Closes everything. In case of POSIX environments, a child process inherits
+ * its parent's opened file descriptors. Which is nowadays considered as one
+ * of the UNIX mistakes. This function closes such inherited file descriptors.
+ * When your C extension needs to have a child process, don't forget to call
+ * this from your child process right before exec.
+ *
+ * @param[in] lowfd Lower bound of FDs (you want STDIN to remain, no?).
+ * @param[in] maxhint Hint of max FDs.
+ * @param[in] noclose_fds A hash, whose keys are an allowlist.
+ *
+ * @internal
+ *
+ * As of writing, in spite of the name, this function does not actually close
+ * anything. It just sets `FD_CLOEXEC` for everything and let `execve(2)` to
+ * atomically close them at once. This is because as far as we know there are
+ * no such platform that has `fork(2)` but lacks `FD_CLOEXEC`.
+ *
+ * Because this function is expected to run on a forked process it is entirely
+ * async-signal-safe.
+ */
+void rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is an rb_cloexec_pipe() + rb_update_max_fd() combo.
+ *
+ * @param[out] pipes Return buffer. Must at least hold 2 elements.
+ * @retval 0 Successful creation of a pipe.
+ * @retval -1 Failure in underlying system call(s).
+ * @post `pipes` is filled with file descriptors.
+ * @post `errno` is set on failure.
+ */
+int rb_pipe(int *pipes);
+
+/**
+ * Queries if the given FD is reserved or not. Occasionally Ruby interpreter
+ * opens files for its own purposes. Use this function to prevent touching
+ * such behind-the-scene descriptors.
+ *
+ * @param[in] fd Target file descriptor.
+ * @retval 1 `fd` is reserved.
+ * @retval 0 Otherwise.
+ */
+int rb_reserved_fd_p(int fd);
+
+/** @alias{rb_reserved_fd_p} */
+#define RB_RESERVED_FD_P(fd) rb_reserved_fd_p(fd)
+
+/**
+ * Opens a file that closes on exec. In case of POSIX environments, a child
+ * process inherits its parent's opened file descriptors. Which is nowadays
+ * considered as one of the UNIX mistakes. This function opens a file
+ * descriptor as `open(2)` does, but additionally instructs the operating
+ * system that we don't want it be seen from child processes.
+ *
+ * @param[in] pathname File path to open.
+ * @param[in] flags Open mode, as in `open(2)`.
+ * @param[in] mode File mode, in case of `O_CREAT`.
+ * @retval -1 `open(2)` failed for some reason.
+ * @retval otherwise An allocated new file descriptor.
+ * @note This function does not raise.
+ *
+ * @internal
+ *
+ * Whether this function can take NULL or not depends on the underlying open(2)
+ * system call implementation but @shyouhei doesn't think it's worth trying.
+ */
+int rb_cloexec_open(const char *pathname, int flags, mode_t mode);
+
+/**
+ * Identical to rb_cloexec_fcntl_dupfd(), except it implies minfd is 3.
+ *
+ * @param[in] oldfd File descriptor to duplicate.
+ * @retval -1 `dup2(2)` failed for some reason.
+ * @retval otherwise An allocated new file descriptor.
+ * @note This function does not raise.
+ */
+int rb_cloexec_dup(int oldfd);
+
+/**
+ * Identical to rb_cloexec_dup(), except you can specify the destination file
+ * descriptor. If the destination is already squatted by another file
+ * descriptor that gets silently closed without any warnings. (This is a spec
+ * requested by POSIX.)
+ *
+ * @param[in] oldfd File descriptor to duplicate.
+ * @param[in] newfd Return value destination.
+ * @retval -1 `dup2(2)` failed for some reason.
+ * @retval newfd An allocated new file descriptor.
+ * @post Whatever sat at `newfd` gets closed with no notifications.
+ * @post In case return value is -1 `newfd` is untouched.
+ * @note This function does not raise.
+ */
+int rb_cloexec_dup2(int oldfd, int newfd);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Opens a pipe with closing on exec. In case of POSIX environments, a child
+ * process inherits its parent's opened file descriptors. Which is nowadays
+ * considered as one of the UNIX mistakes. This function opens a pipe as
+ * `pipe(2)` does, but additionally instructs the operating system that we
+ * don't want the duplicated FDs be seen from child processes.
+ *
+ * @param[out] fildes Return buffer. Must at least hold 2 elements.
+ * @retval 0 Successful creation of a pipe.
+ * @retval -1 Failure in underlying system call(s).
+ * @post `pipes` is filled with file descriptors.
+ * @post `errno` is set on failure.
+ */
+int rb_cloexec_pipe(int fildes[2]);
+
+/**
+ * Duplicates a file descriptor with closing on exec. In case of POSIX
+ * environments, a child process inherits its parent's opened file descriptors.
+ * Which is nowadays considered as one of the UNIX mistakes. This function
+ * duplicates a file descriptor as `dup(2)` does, but additionally instructs
+ * the operating system that we don't want the duplicated FD be seen from child
+ * processes.
+ *
+ * @param[in] fd File descriptor to duplicate.
+ * @param[in] minfd Minimum allowed FD to return.
+ * @retval -1 `dup(2)` failed for some reason.
+ * @retval otherwise An allocated new file descriptor.
+ * @note This function does not raise.
+ *
+ * `minfd` is handy when for instance STDERR is closed but you don't want to
+ * use fd 2.
+ */
+int rb_cloexec_fcntl_dupfd(int fd, int minfd);
+
+/**
+ * Informs the interpreter that the passed fd can be the max. This information
+ * is used from rb_close_before_exec().
+ *
+ * @param[in] fd An open FD, which can be large.
+ */
+void rb_update_max_fd(int fd);
+
+/**
+ * Sets or clears the close-on-exec flag of the passed file descriptor to the
+ * desired state. STDIN, STDOUT, STDERR are the exceptional file descriptors
+ * that shall remain open. All others are to be closed on exec. When a C
+ * extension library opens a file descriptor using anything other than
+ * rb_cloexec_open() etc., that file descriptor shall experience this function.
+ *
+ * @param[in] fd An open file descriptor.
+ */
+void rb_fd_fix_cloexec(int fd);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_IO_H */
diff --git a/include/ruby/internal/intern/load.h b/include/ruby/internal/intern/load.h
new file mode 100644
index 0000000000..9ceb98c2e4
--- /dev/null
+++ b/include/ruby/internal/intern/load.h
@@ -0,0 +1,255 @@
+#ifndef RBIMPL_INTERN_LOAD_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_LOAD_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_f_require().
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* load.c */
+
+/**
+ * Loads and executes the Ruby program in the given file.
+ *
+ * If the path is an absolute path (e.g. starts with `'/'`), the file will be
+ * loaded directly using the absolute path. If the path is an explicit
+ * relative path (e.g. starts with `'./'` or `'../'`), the file will be loaded
+ * using the relative path from the current directory. Otherwise, the file
+ * will be searched for in the library directories listed in the `$LOAD_PATH`.
+ * If the file is found in a directory, this function will attempt to load the
+ * file relative to that directory. If the file is not found in any of the
+ * directories in the `$LOAD_PATH`, the file will be loaded using the relative
+ * path from the current directory.
+ *
+ * If the file doesn't exist when there is an attempt to load it, a LoadError
+ * will be raised.
+ *
+ * If the `wrap` parameter is true, the loaded script will be executed under an
+ * anonymous module, protecting the calling program's global namespace. In no
+ * circumstance will any local variables in the loaded file be propagated to
+ * the loading environment.
+ *
+ * @param[in] path Pathname of a file to load.
+ * @param[in] wrap Either to load under an anonymous module.
+ * @exception rb_eTypeError `path` is not a string.
+ * @exception rb_eArgError `path` is broken as a pathname.
+ * @exception rb_eEncCompatError `path` is incompatible with pathnames.
+ * @exception rb_eLoadError `path` not found.
+ * @exception rb_eException Any exceptions while loading the contents.
+ *
+ * @internal
+ *
+ * It seems this function is under the rule of bootsnap's regime?
+ */
+void rb_load(VALUE path, int wrap);
+
+/**
+ * Identical to rb_load(), except it avoids potential global escapes. Such
+ * global escapes include exceptions, `throw`, `break`, for example.
+ *
+ * It first evaluates the given file as rb_load() does. If no global escape
+ * occurred during the evaluation, it `*state` is set to zero on return.
+ * Otherwise, it sets `*state` to nonzero. If state is `NULL`, it is not set
+ * in both cases.
+ *
+ * @param[in] path Pathname of a file to load.
+ * @param[in] wrap Either to load under an anonymous module.
+ * @param[out] state State of execution.
+ * @post `*state` is set to zero if succeeded. Nonzero otherwise.
+ * @warning You have to clear the error info with `rb_set_errinfo(Qnil)` if
+ * you decide to ignore the caught exception.
+ * @see rb_load
+ * @see rb_protect
+ *
+ * @internal
+ *
+ * Though not a part of our public API, `state` is in fact an
+ * enum ruby_tag_type. You can see the potential "nonzero" values by looking
+ * at vm_core.h.
+ */
+void rb_load_protect(VALUE path, int wrap, int *state);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Queries if the given feature has already been loaded into the execution
+ * context. The "feature" head are things like `"json"` or `"socket"`.
+ *
+ * @param[in] feature Name of a library you want to know about.
+ * @retval 1 Yes there is.
+ * @retval 0 Not yet.
+ */
+int rb_provided(const char *feature);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_provided(), except it additionally returns the "canonical"
+ * name of the loaded feature. This can be handy when for instance you want to
+ * know the actually loaded library is either `foo.rb` or `foo.so`.
+ *
+ * @param[in] feature Name of a library you want to know about.
+ * @param[out] loading Return buffer.
+ * @retval 1 Yes there is.
+ * @retval 0 Not yet.
+ */
+int rb_feature_provided(const char *feature, const char **loading);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Declares that the given feature is already provided by someone else. This
+ * API can be handy when you have an extension called `foo.so` which, when
+ * required, also provides functionality of `bar.so`.
+ *
+ * @param[in] feature Name of a library which had already been provided.
+ * @post No further `require` would search `feature`.
+ */
+void rb_provide(const char *feature);
+
+/**
+ * Identical to rb_require_string(), except it ignores the first argument for
+ * no reason. There seems to be no reason for 3rd party extension libraries to
+ * use it.
+ *
+ * @param[in] self Ignored. Can be anything.
+ * @param[in] feature Name of a feature, e.g. `"json"`.
+ * @exception rb_eLoadError No such feature.
+ * @exception rb_eRuntimeError `$"` is frozen; unable to push.
+ * @retval RUBY_Qtrue The feature is loaded for the first time.
+ * @retval RUBY_Qfalse The feature has already been loaded.
+ * @post `$"` is updated.
+ */
+VALUE rb_f_require(VALUE self, VALUE feature);
+
+/**
+ * Finds and loads the given feature, if absent.
+ *
+ * If the feature is an absolute path (e.g. starts with `'/'`), the feature
+ * will be loaded directly using the absolute path. If the feature is an
+ * explicit relative path (e.g. starts with `'./'` or `'../'`), the feature
+ * will be loaded using the relative path from the current directory.
+ * Otherwise, the feature will be searched for in the library directories
+ * listed in the `$LOAD_PATH`.
+ *
+ * If the feature has the extension `".rb"`, it is loaded as a source file; if
+ * the extension is `".so"`, `".o"`, or `".dll"`, or the default shared library
+ * extension on the current platform, Ruby loads the shared library as a Ruby
+ * extension. Otherwise, Ruby tries adding `".rb"`, `".so"`, and so on to the
+ * name until found. If the file named cannot be found, a LoadError will be
+ * raised.
+ *
+ * For extension libraries the given feature may use any shared library
+ * extension. For example, on Linux you can require `"socket.dll"` to actually
+ * load `socket.so`.
+ *
+ * The absolute path of the loaded file is added to `$LOADED_FEATURES`. A file
+ * will not be loaded again if its path already appears in there.
+ *
+ * Any constants or globals within the loaded source file will be available in
+ * the calling program's global namespace. However, local variables will not
+ * be propagated to the loading environment.
+ *
+ * @param[in] feature Name of a feature, e.g. `"json"`.
+ * @exception rb_eLoadError No such feature.
+ * @exception rb_eRuntimeError `$"` is frozen; unable to push.
+ * @retval RUBY_Qtrue The feature is loaded for the first time.
+ * @retval RUBY_Qfalse The feature has already been loaded.
+ * @post `$"` is updated.
+ */
+VALUE rb_require_string(VALUE feature);
+
+/**
+ * Resolves and returns a symbol of a function in the native extension
+ * specified by the feature and symbol names. Extensions will use this function
+ * to access the symbols provided by other native extensions.
+ *
+ * @param[in] feature Name of a feature, e.g. `"json"`.
+ * @param[in] symbol Name of a symbol defined by the feature.
+ * @return The resolved symbol of a function, defined and externed by the
+ * specified feature. It may be NULL if the feature is not loaded,
+ * the feature is not extension, or the symbol is not found.
+ */
+void *rb_ext_resolve_symbol(const char *feature, const char *symbol);
+
+/**
+ * This macro is to provide backwards compatibility. It provides a way to
+ * define function prototypes and resolving function symbols in a safe way.
+ *
+ * ```CXX
+ * // prototypes
+ * #ifdef HAVE_RB_EXT_RESOLVE_SYMBOL
+ * VALUE *(*other_extension_func)(VALUE,VALUE);
+ * #else
+ * VALUE other_extension_func(VALUE);
+ * #endif
+ *
+ * // in Init_xxx()
+ * #ifdef HAVE_RB_EXT_RESOLVE_SYMBOL
+ * other_extension_func = \
+ * (VALUE(*)(VALUE,VALUE))rb_ext_resolve_symbol(fname, sym_name);
+ * if (other_extension_func == NULL) {
+ * // raise your own error
+ * }
+ * #endif
+ * ```
+ */
+#define HAVE_RB_EXT_RESOLVE_SYMBOL 1
+
+/**
+ * @name extension configuration
+ * @{
+ */
+
+/**
+ * Asserts that the extension library that calls this function is aware of
+ * Ractor. Multiple Ractors run without protecting each other. This doesn't
+ * interface well with C programs, unless designed with an in-depth
+ * understanding of how Ractors work. Extension libraries are shut out from
+ * Ractors by default. This API is to bypass that restriction. Once after it
+ * was called, successive calls to rb_define_method() etc. become definitions
+ * of methods that are aware of Ractors. The amendment would be in effect
+ * until the end of rb_require_string() etc.
+ *
+ * @param[in] flag Either the library is aware of Ractors or not.
+ * @post Methods would be callable form Ractors, if `flag` is true.
+ */
+void rb_ext_ractor_safe(bool flag);
+
+/** @alias{rb_ext_ractor_safe} */
+#define RB_EXT_RACTOR_SAFE(f) rb_ext_ractor_safe(f)
+
+/**
+ * This macro is to provide backwards compatibility. It must be safe to do
+ * something like:
+ *
+ * ```CXX
+ * #ifdef HAVE_RB_EXT_RACTOR_SAFE
+ * rb_ext_ractor_safe(true);
+ * #endif
+ * ```
+ */
+#define HAVE_RB_EXT_RACTOR_SAFE 1
+
+/** @} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_LOAD_H */
diff --git a/include/ruby/internal/intern/marshal.h b/include/ruby/internal/intern/marshal.h
new file mode 100644
index 0000000000..118d78a4a0
--- /dev/null
+++ b/include/ruby/internal/intern/marshal.h
@@ -0,0 +1,112 @@
+#ifndef RBIMPL_INTERN_MARSHAL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_MARSHAL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to rb_mMarshal.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* marshal.c */
+
+/**
+ * Serialises the given object and all its referring objects, to write them
+ * down to the passed port.
+ *
+ * @param[in] obj Target object to dump.
+ * @param[out] port IO-like destination buffer.
+ * @exception rb_eTypeError `obj` cannot be dumped for some reason.
+ * @exception rb_eRuntimeError `obj` was tampered during dumping.
+ * @exception rb_eArgError Traversal too deep.
+ * @return The passed `port` as-is.
+ * @post Serialised representation of `obj` is written to `port`.
+ * @note `port` is basically an IO but StringIO is also possible.
+ */
+VALUE rb_marshal_dump(VALUE obj, VALUE port);
+
+/**
+ * Deserialises a previous output of rb_marshal_dump() into a network of
+ * objects.
+ *
+ * @param[in,out] port Either IO or String.
+ * @exception rb_eTypeError `port` is in unexpected type.
+ * @exception rb_eArgError Contents of `port` is broken.
+ * @return Object(s) rebuilt using the info from `port`.
+ *
+ * SECURITY CONSIDERATIONS
+ * ========================
+ *
+ * @warning By design, rb_marshal_load() can deserialise almost any
+ * class loaded into the Ruby process. In many cases this can
+ * lead to remote code execution if the Marshal data is loaded
+ * from an untrusted source.
+ * @warning As a result, rb_marshal_load() is not suitable as a general
+ * purpose serialisation format and you should never unmarshal
+ * user supplied input or other untrusted data.
+ * @warning If you need to deserialise untrusted data, use JSON or
+ * another serialisation format that is only able to load
+ * simple, 'primitive' types such as String, Array, Hash, etc.
+ * Never allow user input to specify arbitrary types to
+ * deserialise into.
+ */
+VALUE rb_marshal_load(VALUE port);
+
+/**
+ * Marshal format compatibility layer. Over time, classes evolve, so that
+ * their internal data structure change drastically. For instance an instance
+ * of ::rb_cRange was made of ::RUBY_T_OBJECT in 1.x., but in 3.x it is a
+ * ::RUBY_T_STRUCT now. In order to keep binary compatibility, we "fake" the
+ * marshalled representation to stick to old types. This is the API to enable
+ * that manoeuvre. Here is how:
+ *
+ * First, because you are going to keep backwards compatibility, you need to
+ * retain the old implementation of your class. Rename it, and keep the class
+ * somewhere (for instance rb_register_global_address() could help). Next
+ * create your new class. Do whatever you want.
+ *
+ * Then, this is the key point. Create two new "bridge" functions that convert
+ * the structs back and forth:
+ *
+ * - the "dumper" function that takes an instance of the new class, and
+ * returns an instance of the old one. This is called from
+ * rb_marshal_dump(), to keep it possible for old programs to read your new
+ * data.
+ *
+ * - the "loader" function that takes two arguments, new one and old one, in
+ * that order. rb_marshal_load() calls this function when it finds a
+ * representation of the retained old class. The old one passed to this
+ * function is the reconstructed instance of the old class.
+ * Reverse-engineer that to modify the new one, to have the identical
+ * contents.
+ *
+ * Finally, connect all of them using this function.
+ *
+ * @param[in] newclass The class that needs conversion.
+ * @param[in] oldclass Old implementation of `newclass`.
+ * @param[in] dumper Function that converts `newclass` to `oldclass`.
+ * @param[in] loader Function that converts `oldclass` to `newclass`.
+ * @exception rb_eTypeError `newclass` has no allocator.
+ */
+void rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE (*dumper)(VALUE), VALUE (*loader)(VALUE, VALUE));
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_MARSHAL_H */
diff --git a/include/ruby/internal/intern/numeric.h b/include/ruby/internal/intern/numeric.h
new file mode 100644
index 0000000000..30863fb0c8
--- /dev/null
+++ b/include/ruby/internal/intern/numeric.h
@@ -0,0 +1,208 @@
+#ifndef RBIMPL_INTERN_NUMERIC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_NUMERIC_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cNumeric.
+ */
+#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define RB_NUM_COERCE_FUNCS_NEED_OPID 1
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* numeric.c */
+
+RBIMPL_ATTR_NORETURN()
+RBIMPL_ATTR_COLD()
+/**
+ * Just always raises an exception.
+ *
+ * @exception rb_eZeroDivError Division by zero error.
+ */
+void rb_num_zerodiv(void);
+
+/**
+ * @name Coercion operators.
+ *
+ * What is a coercion? Well Ruby is basically an OOPL but it also has
+ * arithmetic operators. They are implemented in OO manners. For instance
+ * `a+b` is a binary operation `+`, whose receiver is `a`, and whose (sole)
+ * argument is `b`.
+ *
+ * The problem is, you often want `a+b == b+a` to hold. That is easy if both
+ * `a` and `b` belongs to the same class... Ensuring `1 + 2 == 2 + 1` is kind
+ * of intuitive. But if you want `1.0 + 2 == 2 + 1.0`, things start getting
+ * complicated. `1.0+2` is `Float#+`, while `2+1.0` is `Integer#+`. In order
+ * to achieve the equality Float's and Integer's methods must agree with their
+ * behaviours.
+ *
+ * Now. Floats versus Integers situation is still controllable because they
+ * are both built-in. But in Ruby you can define your own numeric classes.
+ * BigDecimal, which is a rubygems gem distributed along with the interpreter,
+ * is one of such examples. Rational was another such example before. In
+ * short you cannot create list of all possible combination of the classes that
+ * could be the operand of `+` operator. Then how do we achieve the
+ * commutativity?
+ *
+ * Here comes the concept of coercion. If a definition of an operator
+ * encounters an object which is unknown to the author, just assumes that the
+ * unknown object knows how to handle the situation. So for instance when
+ * `1+x` has unknown `x`, it lets the `x` handle this.
+ *
+ * ```ruby
+ * class Foo
+ * def +(x)
+ * if we_know_what_is_x? then
+ * ... # handle here
+ * else
+ * y, z = x.coerce self
+ * return y + z
+ * end
+ * end
+ * end
+ * ```
+ *
+ * The `x.coerce` method returns a 2-element array which are "casted" versions
+ * of `x` and `self`.
+ *
+ * @{
+ */
+
+/**
+ * Coerced binary operation. This function first coerces the two objects, then
+ * applies the operation.
+ *
+ * @param[in] lhs LHS operand.
+ * @param[in] rhs RHS operand.
+ * @param[in] op Operator method name.
+ * @exception rb_eTypeError Coercion failed for some reason.
+ * @return `lhs op rhs`, in a coerced way.
+ */
+VALUE rb_num_coerce_bin(VALUE lhs, VALUE rhs, ID op);
+
+/**
+ * Identical to rb_num_coerce_bin(), except for return values. This function
+ * best suits for comparison operators e.g. `<=>`.
+ *
+ * @param[in] lhs LHS operand.
+ * @param[in] rhs RHS operand.
+ * @param[in] op Operator method name.
+ * @retval RUBY_Qnil Coercion failed for some reason.
+ * @retval otherwise `lhs op rhs`, in a coerced way.
+ */
+VALUE rb_num_coerce_cmp(VALUE lhs, VALUE rhs, ID op);
+
+/**
+ * Identical to rb_num_coerce_cmp(), except for return values. This function
+ * best suits for relationship operators e.g. `<=`.
+ *
+ * @param[in] lhs LHS operand.
+ * @param[in] rhs RHS operand.
+ * @param[in] op Operator method name.
+ * @exception rb_eArgError Coercion failed for some reason.
+ * @return `lhs op rhs`, in a coerced way.
+ */
+VALUE rb_num_coerce_relop(VALUE lhs, VALUE rhs, ID op);
+
+/**
+ * This one is optimised for bitwise operations, but the API is identical to
+ * rb_num_coerce_bin().
+ *
+ * @param[in] lhs LHS operand.
+ * @param[in] rhs RHS operand.
+ * @param[in] op Operator method name.
+ * @exception rb_eArgError Coercion failed for some reason.
+ * @return `lhs op rhs`, in a coerced way.
+ */
+VALUE rb_num_coerce_bit(VALUE lhs, VALUE rhs, ID op);
+
+/** @} */
+
+/**
+ * Converts a numeric value into a Fixnum. This is not a preserving
+ * conversion; for instance 1.5 would be converted into 1.
+ *
+ * @param[in] val A numeric object.
+ * @exception rb_eTypeError No conversion from `val` to Integer.
+ * @exception rb_eRangeError `val` out of range.
+ * @return A fixnum converted from `val`.
+ *
+ * @internal
+ *
+ * This seems used from nowhere?
+ */
+VALUE rb_num2fix(VALUE val);
+
+/**
+ * Generates a place-value representation of the given Fixnum, with given
+ * radix.
+ *
+ * @param[in] val A fixnum to stringify.
+ * @param[in] base `2` to `36` inclusive for each radix.
+ * @exception rb_eArgError `base` is out of range.
+ * @return An instance of ::rb_cString representing `val`.
+ * @pre `val` must be a Fixnum (no checks performed).
+ */
+VALUE rb_fix2str(VALUE val, int base);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Compares two `double`s. Handy when implementing a spaceship operator.
+ *
+ * @param[in] lhs A value.
+ * @param[in] rhs Another value.
+ * @retval RB_INT2FIX(-1) `lhs` is "bigger than" `rhs`.
+ * @retval RB_INT2FIX(1) `rhs` is "bigger than" `lhs`.
+ * @retval RB_INT2FIX(0) They are equal.
+ * @retval RUBY_Qnil Not comparable, e.g. NaN.
+ */
+VALUE rb_dbl_cmp(double lhs, double rhs);
+
+/**
+ * Raises the passed `x` to the power of `y`.
+ *
+ * @note The return value can be really big.
+ * @note Also the return value can be really small, in case `x` is a
+ * negative number.
+ * @param[in] x A number.
+ * @param[in] y Another number.
+ * @retval Inf Cannot express the result.
+ * @retval 1 Either `y` is 0 or `x` is 1.
+ * @retval otherwise An instance of ::rb_cInteger whose value is `x ** y`.
+ *
+ * @internal
+ *
+ * This function returns Infinity when `y` is big enough not to fit into a
+ * Fixnum. Warning is issued then.
+ */
+RUBY_EXTERN VALUE rb_int_positive_pow(long x, unsigned long y);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_NUMERIC_H */
diff --git a/include/ruby/internal/intern/object.h b/include/ruby/internal/intern/object.h
new file mode 100644
index 0000000000..3897639a0a
--- /dev/null
+++ b/include/ruby/internal/intern/object.h
@@ -0,0 +1,501 @@
+#ifndef RBIMPL_INTERN_OBJECT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_OBJECT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cObject.
+ */
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * This macro is (used but) mysterious. Why on earth do we need this?
+ *
+ * - `obj != orig` check is done anyways inside of rb_obj_init_copy().
+ * - rb_obj_init_copy() returns something. No need are there to add `, 1`.
+ */
+#define RB_OBJ_INIT_COPY(obj, orig) \
+ ((obj) != (orig) && (rb_obj_init_copy((obj), (orig)), 1))
+/** @old{RB_OBJ_INIT_COPY} */
+#define OBJ_INIT_COPY(obj, orig) RB_OBJ_INIT_COPY(obj, orig)
+
+/* object.c */
+
+/**
+ * Identical to rb_class_new_instance(), except it passes the passed keywords
+ * if any to the `#initialize` method.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] klass An instance of ::rb_cClass.
+ * @exception rb_eTypeError `klass`'s allocator is undefined.
+ * @exception rb_eException Any exceptions can happen inside.
+ * @return An allocated new instance of `klass`.
+ * @note This is _the_ implementation of `Object.new`.
+ */
+VALUE rb_class_new_instance_pass_kw(int argc, const VALUE *argv, VALUE klass);
+
+/**
+ * Allocates, then initialises an instance of the given class. It first calls
+ * the passed class' allocator to obtain an uninitialised object, then calls
+ * its initialiser with the remaining arguments.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arguments passed to `#initialize`.
+ * @param[in] klass An instance of ::rb_cClass.
+ * @exception rb_eTypeError `klass`'s allocator is undefined.
+ * @exception rb_eException Any exceptions can happen inside.
+ * @return An allocated new instance of `klass`.
+ */
+VALUE rb_class_new_instance(int argc, const VALUE *argv, VALUE klass);
+
+/**
+ * Identical to rb_class_new_instance(), except you can specify how to handle
+ * the last element of the given array.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] klass An instance of ::rb_cClass.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eTypeError `klass`'s allocator is undefined.
+ * @exception rb_eException Any exceptions can happen inside.
+ * @return An allocated new instance of `klass`.
+ */
+VALUE rb_class_new_instance_kw(int argc, const VALUE *argv, VALUE klass, int kw_splat);
+
+/**
+ * Checks for equality of the passed objects, in terms of `Object#eql?`.
+ *
+ * @param[in] lhs Comparison left hand side.
+ * @param[in] rhs Comparison right hand side.
+ * @retval non-zero They are equal.
+ * @retval 0 Otherwise.
+ * @note This function actually calls `lhs.eql?(rhs)` so you cannot
+ * implement your class' `#eql?` method using it.
+ */
+int rb_eql(VALUE lhs, VALUE rhs);
+
+/**
+ * Generates a textual representation of the given object.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @return An instance of ::rb_cString that represents `obj`.
+ * @note This is the default implementation of `Object#to_s` that each
+ * subclasses want to override.
+ */
+VALUE rb_any_to_s(VALUE obj);
+
+/**
+ * Generates a human-readable textual representation of the given object. This
+ * is largely similar to Ruby level `Object#inspect` but not the same; it
+ * additionally escapes the inspection result so that the string be compatible
+ * with that of default internal (or default external, if absent).
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @return An instance of ::rb_cString that represents `obj`.
+ */
+VALUE rb_inspect(VALUE obj);
+
+/**
+ * Queries if the given object is a direct instance of the given class.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @param[in] klass An instance of ::rb_cModule.
+ * @exception rb_eTypeError `klass` is neither module nor class.
+ * @retval RUBY_Qtrue `obj` is an instance of `klass`.
+ * @retval RUBY_Qfalse Otherwise.
+ */
+VALUE rb_obj_is_instance_of(VALUE obj, VALUE klass);
+
+/**
+ * Queries if the given object is an instance (of possibly descendants) of the
+ * given class.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @param[in] klass An instance of ::rb_cModule.
+ * @exception rb_eTypeError `klass` is neither module nor class.
+ * @retval RUBY_Qtrue `obj` is a `klass`.
+ * @retval RUBY_Qfalse Otherwise.
+ */
+VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass);
+
+/**
+ * Allocates an instance of the given class.
+ *
+ * @param[in] klass A class to instantiate.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @return An allocated, not yet initialised instance of `klass`.
+ * @note It calls the allocator defined by rb_define_alloc_func(). You
+ * cannot use this function to define an allocator. Use
+ * TypedData_Make_Struct or others, instead.
+ * @note Usually prefer rb_class_new_instance() to rb_obj_alloc() and
+ * rb_obj_call_init().
+ * @see rb_class_new_instance()
+ * @see rb_obj_call_init()
+ * @see rb_define_alloc_func()
+ * @see #TypedData_Make_Struct
+ */
+VALUE rb_obj_alloc(VALUE klass);
+
+/**
+ * Produces a shallow copy of the given object. Its list of instance variables
+ * are copied, but not the objects they reference. It also copies the frozen
+ * value state.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @exception rb_eException `#initialize_copy` can raise anything.
+ * @return A "clone" of `obj`.
+ *
+ * @internal
+ *
+ * Unlike ruby-level `Object#clone`, there is no way to control the frozen-ness
+ * of the return value.
+ */
+VALUE rb_obj_clone(VALUE obj);
+
+/**
+ * Duplicates the given object. This does almost the same thing as
+ * rb_obj_clone() do. However it does not copy the singleton class (if any).
+ * It also doesn't copy frozen-ness.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @exception rb_eException `#initialize_copy` can raise anything.
+ * @return A shallow copy of `obj`.
+ */
+VALUE rb_obj_dup(VALUE obj);
+
+/**
+ * Default implementation of `#initialize_copy`, `#initialize_dup` and
+ * `#initialize_clone`. It does almost nothing. Just raises exceptions for
+ * checks.
+ *
+ * @param[in] dst The destination object.
+ * @param[in] src The source object.
+ * @exception rb_eFrozenError `dst` is frozen.
+ * @exception rb_eTypeError `dst` and `src` have different classes.
+ * @return Always returns `dst`.
+ */
+VALUE rb_obj_init_copy(VALUE src, VALUE dst);
+
+/**
+ * Just calls rb_obj_freeze_inline() inside. Does this make any sens to
+ * extension libraries?
+ *
+ * @param[out] obj Object to freeze.
+ * @return Verbatim `obj`.
+ */
+VALUE rb_obj_freeze(VALUE obj);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Just calls RB_OBJ_FROZEN() inside. Does this make any sens to extension
+ * libraries?
+ *
+ * @param[in] obj Object in question.
+ * @retval RUBY_Qtrue Yes it is.
+ * @retval RUBY_Qfalse No it isn't.
+ */
+VALUE rb_obj_frozen_p(VALUE obj);
+
+/* gc.c */
+
+/**
+ * Finds or creates an integer primary key of the given object. In the old
+ * days this function was a purely arithmetic operation that maps the
+ * underlying memory address where the object resides into a Ruby's integer.
+ * Some time around 2.x this changed. It no longer relates its return values
+ * to C level pointers. This function assigns some random number to the given
+ * object if absent. The same number will be returned on all subsequent
+ * requests. No two active objects share a number.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @return An instance of ::rb_cInteger which is an "identifier" of `obj`.
+ *
+ * @internal
+ *
+ * The "some random number" is in fact a monotonic-increasing process-global
+ * unique integer, much like an `INTEGER AUTO_INCREMENT PRIMARY KEY` column in
+ * a MySQL table.
+ */
+VALUE rb_obj_id(VALUE obj);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Identical to rb_obj_id(), except it hesitates from allocating a new instance
+ * of ::rb_cInteger. rb_obj_id() could allocate ::RUBY_T_BIGNUM objects. That
+ * allocation might perhaps impact negatively. On such situations, this
+ * function instead returns one-shot temporary small integers that need no
+ * allocations at all. The values are guaranteed unique at the moment, but no
+ * future promise is made; could be reused. Use of this API should be very
+ * instant. It is a failure to store the returned integer to somewhere else.
+ *
+ * In short it is difficult to use.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @return An instance of ::rb_cInteger unique at the moment.
+ *
+ * @internal
+ *
+ * This is roughly the old behaviour of rb_obj_id().
+ */
+VALUE rb_memory_id(VALUE obj);
+
+/* object.c */
+
+RBIMPL_ATTR_PURE()
+/**
+ * Finds a "real" class. As the name implies there are class objects that are
+ * surreal. This function takes a class, traverses its ancestry tree, and
+ * returns its nearest ancestor which is neither a module nor a singleton
+ * class.
+ *
+ * @param[in] klass An instance of ::rb_cClass.
+ * @retval RUBY_Qfalse No real class in `klass`' ancestry tree.
+ * @retval klass `klass` itself is a real class.
+ * @retval otherwise Nearest ancestor of `klass` who is real.
+ */
+VALUE rb_class_real(VALUE klass);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Determines if the given two modules are relatives.
+ *
+ * @param[in] scion Possible subclass.
+ * @param[in] ascendant Possible superclass.
+ * @exception rb_eTypeError `ascendant` is not a module.
+ * @retval RUBY_Qtrue `scion` inherits, or is equal to `ascendant`.
+ * @retval RUBY_Qfalse `ascendant` inherits `scion`.
+ * @retval RUBY_Qnil They are not relatives.
+ */
+VALUE rb_class_inherited_p(VALUE scion, VALUE ascendant);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries the parent of the given class.
+ *
+ * @param[in] klass A child class.
+ * @exception rb_eTypeError `klass` is a `Class.allocate`.
+ * @retval RUBY_Qfalse `klass` has no superclass.
+ * @retval otherwise `klass`' superclass.
+ *
+ * @internal
+ *
+ * Is there any class except ::rb_cBasicObject, that has no superclass?
+ */
+VALUE rb_class_superclass(VALUE klass);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Converts an object into another type. Calls the specified conversion method
+ * if necessary.
+ *
+ * @param[in] val An object to convert.
+ * @param[in] type A value of enum ::ruby_value_type.
+ * @param[in] name Name to display on error (e.g. "Array").
+ * @param[in] mid Conversion method (e.g. "to_ary").
+ * @exception rb_eTypeError Failed to convert.
+ * @return An object of the specified type.
+ */
+VALUE rb_convert_type(VALUE val, int type, const char *name, const char *mid);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_convert_type(), except it returns ::RUBY_Qnil instead of
+ * raising exceptions, in case of conversion failure. It still raises
+ * exceptions for various reasons, like when the conversion method itself
+ * raises, though.
+ *
+ * @param[in] val An object to convert.
+ * @param[in] type A value of enum ::ruby_value_type.
+ * @param[in] name Name to display on error (e.g. "Array").
+ * @param[in] mid Conversion method (e.g. "to_ary").
+ * @exception rb_eTypeError The `mid` does not generate `type`.
+ * @retval RUBY_Qnil No conversion defined.
+ * @retval otherwise An object of the specified type.
+ */
+VALUE rb_check_convert_type(VALUE val, int type, const char *name, const char *mid);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_check_convert_type(), except the return value type is fixed
+ * to ::rb_cInteger.
+ *
+ * @param[in] val An object to convert.
+ * @param[in] mid Conversion method (e.g. "to_ary").
+ * @exception rb_eTypeError The `mid` does not generate an integer.
+ * @retval RUBY_Qnil No conversion defined.
+ * @retval otherwise An instance of ::rb_cInteger.
+ */
+VALUE rb_check_to_integer(VALUE val, const char *mid);
+
+/**
+ * This is complicated.
+ *
+ * - When the passed object is already an instance of ::rb_cFloat, just
+ * returns it as-is.
+ *
+ * - When the passed object is something numeric, the function tries to
+ * convert it using `#to_f` method.
+ *
+ * - If that conversion fails (this happens for instance when the numeric
+ * is a complex) it returns ::RUBY_Qnil.
+ *
+ * - Otherwise returns the conversion result.
+ *
+ * - Otherwise it also returns ::RUBY_Qnil.
+ *
+ * @param[in] val An object to convert.
+ * @retval RUBY_Qnil Conversion from `val` to float is undefined.
+ * @retval otherwise Converted result.
+ */
+VALUE rb_check_to_float(VALUE val);
+
+/**
+ * Identical to rb_check_to_int(), except it raises in case of conversion
+ * mismatch.
+ *
+ * @param[in] val An object to convert.
+ * @exception rb_eTypeError `#to_int` does not generate an integer.
+ * @return An instance of ::rb_cInteger.
+ */
+VALUE rb_to_int(VALUE val);
+
+/**
+ * Identical to rb_check_to_integer(), except it uses `#to_int` for conversion.
+ *
+ * @param[in] val An object to convert.
+ * @exception rb_eTypeError `#to_int` does not return an integer.
+ * @retval RUBY_Qnil No conversion defined.
+ * @retval otherwise An instance of ::rb_cInteger.
+ */
+VALUE rb_check_to_int(VALUE val);
+
+/**
+ * This is the logic behind `Kernel#Integer`. Numeric types are converted
+ * directly, with floating point numbers being truncated. Strings are
+ * interpreted strictly; only leading/trailing whitespaces, plus/minus sign,
+ * radix indicators such as `0x`, digits, and underscores are allowed.
+ * Anything else are converted by first trying `#to_int`, then `#to_i`.
+ *
+ * This is slightly stricter than `String#to_i`.
+ *
+ * @param[in] val An object to convert.
+ * @exception rb_eArgError Malformed `val` passed.
+ * @exception rb_eTypeError No conversion defined.
+ * @return An instance of ::rb_cInteger.
+ */
+VALUE rb_Integer(VALUE val);
+
+/**
+ * Identical to rb_check_to_float(), except it raises on error.
+ *
+ * @param[in] val An object to convert.
+ * @exception rb_eTypeError No conversion defined.
+ * @return An instance of ::rb_cFloat.
+ */
+VALUE rb_to_float(VALUE val);
+
+/**
+ * This is the logic behind `Kernel#Float`. Numeric types are converted
+ * directly to the nearest value that a Float can represent. Strings are
+ * interpreted strictly; only leading/trailing whitespaces are allowed except
+ * what `strtod` understands. Anything else are converted using `#to_f`.
+ *
+ * This is slightly stricter than `String#to_f`.
+ *
+ * @param[in] val An object to convert.
+ * @exception rb_eArgError Malformed `val` passed.
+ * @exception rb_eTypeError No conversion defined.
+ * @return An instance of ::rb_cFloat.
+ */
+VALUE rb_Float(VALUE val);
+
+/**
+ * This is the logic behind `Kernel#String`. Arguments are converted by first
+ * trying `#to_str`, then `#to_s`.
+ *
+ * @param[in] val An object to convert.
+ * @exception rb_eTypeError No conversion defined.
+ * @return An instance of ::rb_cString.
+ */
+VALUE rb_String(VALUE val);
+
+/**
+ * This is the logic behind `Kernel#Array`. Arguments are converted by first
+ * trying `#to_ary`, then `#to_a`, and if both failed, returns an array of
+ * length 1 that contains the passed argument as the sole contents.
+ *
+ * @param[in] val An object to convert.
+ * @return An instance of ::rb_cArray.
+ */
+VALUE rb_Array(VALUE val);
+
+/**
+ * This is the logic behind `Kernel#Hash`. Arguments are converted by first
+ * trying `#to_hash`. if it failed, and the argument is either ::RUBY_Qnil or
+ * an empty array, returns an empty hash. Otherwise an exception is raised.
+ *
+ * @param[in] val An object to convert.
+ * @exception rb_eTypeError No conversion defined.
+ * @return An instance of ::rb_cHash.
+ */
+VALUE rb_Hash(VALUE val);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Converts a textual representation of a real number into a numeric, which is
+ * the nearest value that the return type can represent, of the value that the
+ * argument represents. This is in fact a 2-in-1 function whose behaviour can
+ * be controlled using the second (mode) argument. If the mode is zero, this
+ * function is in "historical" mode which only understands "floating-constant"
+ * defined at ISO/IEC 9899:1990 section 6.1.3.1. If the mode is nonzero, it is
+ * in "extended" mode, which also accepts "hexadecimal-floating-constant"
+ * defined at ISO/IEC 9899:2018 section 6.4.4.2.
+ *
+ * @param[in] str A textual representation of a real number.
+ * @param[in] mode Conversion mode, as described above.
+ * @exception rb_eArgError Malformed `str` passed.
+ * @see https://bugs.ruby-lang.org/issues/2969
+ * @note Null pointers are allowed, and it returns 0.0 then.
+ */
+double rb_cstr_to_dbl(const char *str, int mode);
+
+/**
+ * Identical to rb_cstr_to_dbl(), except it accepts a Ruby's string instead of
+ * C's.
+ *
+ * @param[in] str A textual representation of a real number.
+ * @param[in] mode Conversion mode, as described in rb_cstr_to_dbl().
+ * @exception rb_eArgError Malformed `str` passed.
+ * @see https://bugs.ruby-lang.org/issues/2969
+ */
+double rb_str_to_dbl(VALUE str, int mode);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_OBJECT_H */
diff --git a/include/ruby/internal/intern/parse.h b/include/ruby/internal/intern/parse.h
new file mode 100644
index 0000000000..7c4e9925b9
--- /dev/null
+++ b/include/ruby/internal/intern/parse.h
@@ -0,0 +1,194 @@
+#ifndef RBIMPL_INTERN_PARSE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_PARSE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cSymbol.
+ */
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* symbol.c */
+
+/**
+ * Calculates an ID of attribute writer. For instance it returns `:foo=` when
+ * passed `:foo`.
+ *
+ * @param[in] id An id.
+ * @exception rb_eNameError `id` is not for attributes (e.g. operator).
+ * @return Calculated name of attribute writer.
+ */
+ID rb_id_attrset(ID id);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Classifies the given ID, then sees if it is a constant. In case an ID is in
+ * Unicode (likely), its "constant"-ness is determined if its first character
+ * is either upper case or title case. Otherwise it is detected if case-
+ * folding the first character changes its case or not.
+ *
+ * @param[in] id An id to classify.
+ * @retval 1 It is a constant.
+ * @retval 0 It isn't.
+ */
+int rb_is_const_id(ID id);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Classifies the given ID, then sees if it is a global variable. A global
+ * variable must start with `$`.
+ *
+ * @param[in] id An id to classify.
+ * @retval 1 It is a global variable.
+ * @retval 0 It isn't.
+ */
+int rb_is_global_id(ID id);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Classifies the given ID, then sees if it is an instance variable. An
+ * instance variable must start with `@`, but not `@@`.
+ *
+ * @param[in] id An id to classify.
+ * @retval 1 It is an instance variable.
+ * @retval 0 It isn't.
+ */
+int rb_is_instance_id(ID id);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Classifies the given ID, then sees if it is an attribute writer. An
+ * attribute writer is otherwise a local variable, except it ends with `=`.
+ *
+ * @param[in] id An id to classify.
+ * @retval 1 It is an attribute writer.
+ * @retval 0 It isn't.
+ */
+int rb_is_attrset_id(ID id);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Classifies the given ID, then sees if it is a class variable. A class
+ * variable is must start with `@@`.
+ *
+ * @param[in] id An id to classify.
+ * @retval 1 It is a class variable.
+ * @retval 0 It isn't.
+ */
+int rb_is_class_id(ID id);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Classifies the given ID, then sees if it is a local variable. A local
+ * variable starts with a lowercase character, followed by some alphanumeric
+ * characters or `_`, then ends with anything other than `!`, `?`, or `=`.
+ *
+ * @param[in] id An id to classify.
+ * @retval 1 It is a local variable.
+ * @retval 0 It isn't.
+ */
+int rb_is_local_id(ID id);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Classifies the given ID, then sees if it is a junk ID. An ID with no
+ * special syntactic structure is considered junk. This category includes for
+ * instance punctuation.
+ *
+ * @param[in] id An id to classify.
+ * @retval 1 It is a junk.
+ * @retval 0 It isn't.
+ */
+int rb_is_junk_id(ID);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Sees if the passed C string constructs a valid syntactic symbol. Invalid
+ * ones for instance includes whitespaces.
+ *
+ * @param[in] str A C string to check.
+ * @retval 1 It is a valid symbol name.
+ * @retval 0 It is invalid as a symbol name.
+ */
+int rb_symname_p(const char *str);
+
+/* vm.c */
+
+/**
+ * Queries the last match, or `Regexp.last_match`, or the `$~`. You don't have
+ * to use it, because in reality you can get `$~` using rb_gv_get() as usual.
+ *
+ * @retval RUBY_Qnil The method has not ran a regular expression.
+ * @retval otherwise An instance of ::rb_cMatch.
+ */
+VALUE rb_backref_get(void);
+
+/**
+ * Updates `$~`. You don't have to use it, because in reality you can set `$~`
+ * using rb_gv_set() as usual.
+ *
+ * @param[in] md Arbitrary Ruby object.
+ * @post The passed object is assigned to `$~`.
+ *
+ * @internal
+ *
+ * Yes, this function bypasses the Check_Type() that would normally prevent
+ * evil souls from assigning evil objects to `$~`. Use of this function is a
+ * really bad smell.
+ */
+void rb_backref_set(VALUE md);
+
+/**
+ * Queries the last line, or the `$_`. You don't have to use it, because in
+ * reality you can get `$_` using rb_gv_get() as usual.
+ *
+ * @retval RUBY_Qnil There has never been a "line" yet.
+ * @retval otherwise The last set `$_` value.
+ */
+VALUE rb_lastline_get(void);
+
+/**
+ * Updates `$_`. You don't have to use it, because in reality you can set `$_`
+ * using rb_gv_set() as usual.
+ *
+ * @param[in] str Arbitrary Ruby object.
+ * @post The passed object is assigned to `$_`.
+ *
+ * @internal
+ *
+ * Unlike `$~`, you can assign non-strings to `$_`, even from ruby scripts.
+ */
+void rb_lastline_set(VALUE str);
+
+/* symbol.c */
+
+/**
+ * Collects every single bits of symbols that have ever interned in the entire
+ * history of the current process.
+ *
+ * @return An array that contains all symbols that have ever existed.
+ */
+VALUE rb_sym_all_symbols(void);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_PARSE_H */
diff --git a/include/ruby/internal/intern/proc.h b/include/ruby/internal/intern/proc.h
new file mode 100644
index 0000000000..2635d672eb
--- /dev/null
+++ b/include/ruby/internal/intern/proc.h
@@ -0,0 +1,357 @@
+#ifndef RBIMPL_INTERN_PROC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_PROC_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cProc.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/iterator.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* proc.c */
+
+/**
+ * Constructs a Proc object from implicitly passed components. When a ruby
+ * method is called with a block, that block is not explicitly passed around
+ * using C level function parameters. This function gathers all the necessary
+ * info to turn them into a Ruby level instance of ::rb_cProc.
+ *
+ * @exception rb_eArgError There is no passed block.
+ * @return An instance of ::rb_cProc.
+ */
+VALUE rb_block_proc(void);
+
+/**
+ * Identical to rb_proc_new(), except it returns a lambda.
+ *
+ * @exception rb_eArgError There is no passed block.
+ * @return An instance of ::rb_cProc.
+ */
+VALUE rb_block_lambda(void);
+
+/**
+ * This is an rb_iterate() + rb_block_proc() combo.
+ *
+ * ```CXX
+ * VALUE
+ * my_own_iterator(RB_BLOCK_CALL_FUNC_ARGLIST(y, c))
+ * {
+ * const auto plus = rb_intern("+");
+ * return rb_funcall(c, plus, 1, y);
+ * }
+ *
+ * VALUE
+ * my_own_method(VALUE self)
+ * {
+ * return rb_proc_new(my_own_iterator, self);
+ * }
+ * ```
+ *
+ * @param[in] func A backend function of a proc.
+ * @param[in] callback_arg Passed to `func`'s callback_arg.
+ * @return A C-backended proc object.
+ *
+ */
+VALUE rb_proc_new(rb_block_call_func_t func, VALUE callback_arg);
+
+/**
+ * Queries if the given object is a proc.
+ *
+ * @note This is about the object's data structure, not its class etc.
+ * @param[in] recv Object in question.
+ * @retval RUBY_Qtrue It is a proc.
+ * @retval RUBY_Qfalse Otherwise.
+ */
+VALUE rb_obj_is_proc(VALUE recv);
+
+/**
+ * Evaluates the passed proc with the passed arguments.
+ *
+ * @param[in] recv The proc to call.
+ * @param[in] args An instance of ::RArray which is the arguments.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the proc evaluates to.
+ */
+VALUE rb_proc_call(VALUE recv, VALUE args);
+
+/**
+ * Identical to rb_proc_call(), except you can specify how to handle the last
+ * element of the given array.
+ *
+ * @param[in] recv The proc to call.
+ * @param[in] args An instance of ::RArray which is the arguments.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `args`' last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `args`' last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the proc evaluates to.
+ */
+VALUE rb_proc_call_kw(VALUE recv, VALUE args, int kw_splat);
+
+/**
+ * Identical to rb_proc_call(), except you can additionally pass another proc
+ * object, as a block. Nowadays procs can take blocks:
+ *
+ * ```ruby
+ * l = -> (positional, optional=nil, *rest, kwarg:, **kwrest, &block) {
+ * # ... how can we pass this `&block`? ^^^^^^
+ * }
+ * ```
+ *
+ * And this function is to pass one to such procs.
+ *
+ * @param[in] recv The proc to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of proc arguments.
+ * @param[in] proc Proc as a passed block.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the proc evaluates to.
+ */
+VALUE rb_proc_call_with_block(VALUE recv, int argc, const VALUE *argv, VALUE proc);
+
+/**
+ * Identical to rb_proc_call_with_block(), except you can specify how to handle
+ * the last element of the given array. It can also be seen as a routine
+ * identical to rb_proc_call_kw(), except you can additionally pass another
+ * proc object as a block.
+ *
+ * @param[in] recv The proc to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of proc arguments.
+ * @param[in] proc Proc as a passed block.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `args`' last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `args`' last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the proc evaluates to.
+ */
+VALUE rb_proc_call_with_block_kw(VALUE recv, int argc, const VALUE *argv, VALUE proc, int kw_splat);
+
+/**
+ * Queries the number of mandatory arguments of the given Proc. If its block
+ * is declared to take no arguments, returns `0`. If the block is known to
+ * take exactly `n` arguments, returns `n`. If the block has optional
+ * arguments, returns `-n-1`, where `n` is the number of mandatory arguments,
+ * with the exception for blocks that are not lambdas and have only a finite
+ * number of optional arguments; in this latter case, returns `n`. Keyword
+ * arguments will be considered as a single additional argument, that argument
+ * being mandatory if any keyword argument is mandatory.
+ *
+ * @param[in] recv Target Proc object.
+ * @retval 0 It takes no arguments.
+ * @retval >0 It takes exactly this number of arguments.
+ * @retval <0 It takes optional arguments.
+ */
+int rb_proc_arity(VALUE recv);
+
+/**
+ * Queries if the given object is a lambda. Instances of ::rb_cProc are either
+ * lambda or proc. They differ in several points. This function can
+ * distinguish them without actually evaluating their contents.
+ *
+ * @param[in] recv Target proc object.
+ * @retval RUBY_Qtrue It is a lambda.
+ * @retval RUBY_Qfalse Otherwise.
+ */
+VALUE rb_proc_lambda_p(VALUE recv);
+
+/**
+ * Snapshots the current execution context and turn it into an instance of
+ * ::rb_cBinding.
+ *
+ * @return An instance of ::rb_cBinding.
+ */
+VALUE rb_binding_new(void);
+
+/**
+ * Creates a method object. A method object is a proc-like object that you can
+ * "call". Note that a method object snapshots the method at the time the
+ * object is created:
+ *
+ * ```ruby
+ * class Foo
+ * def foo
+ * return 1
+ * end
+ * end
+ *
+ * obj = Foo.new.method(:foo)
+ *
+ * class Foo
+ * def foo
+ * return 2
+ * end
+ * end
+ *
+ * obj.call # => 1, not 2.
+ * ```
+ *
+ * @param[in] recv Receiver of the method.
+ * @param[in] mid Method name, in either String or Symbol.
+ * @exception rb_eNoMethodError No such method.
+ * @return An instance of ::rb_cMethod.
+ */
+VALUE rb_obj_method(VALUE recv, VALUE mid);
+
+/**
+ * Queries if the given object is a method.
+ *
+ * @note This is about the object's data structure, not its class etc.
+ * @param[in] recv Object in question.
+ * @retval RUBY_Qtrue It is a method.
+ * @retval RUBY_Qfalse Otherwise.
+ */
+VALUE rb_obj_is_method(VALUE recv);
+
+/**
+ * Evaluates the passed method with the passed arguments.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] recv The method object to call.
+ * @exception rb_eTypeError `recv` is not a method.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method returns.
+ */
+VALUE rb_method_call(int argc, const VALUE *argv, VALUE recv);
+
+/**
+ * Identical to rb_method_call(), except you can specify how to handle the last
+ * element of the given array.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] recv The method object to call.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `args`' last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `args`' last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eTypeError `recv` is not a method.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method returns.
+ */
+VALUE rb_method_call_kw(int argc, const VALUE *argv, VALUE recv, int kw_splat);
+
+/**
+ * Identical to rb_proc_call(), except you can additionally pass a proc as a
+ * block.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] recv The method object to call.
+ * @param[in] proc Proc as a passed block.
+ * @exception rb_eTypeError `recv` is not a method.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method returns.
+ */
+VALUE rb_method_call_with_block(int argc, const VALUE *argv, VALUE recv, VALUE proc);
+
+/**
+ * Identical to rb_method_call_with_block(), except you can specify how to
+ * handle the last element of the given array. It can also be seen as a
+ * routine identical to rb_method_call_kw(), except you can additionally pass
+ * another proc object as a block.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] recv The method object to call.
+ * @param[in] proc Proc as a passed block.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `args`' last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `args`' last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @exception rb_eTypeError `recv` is not a method.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method returns.
+ */
+VALUE rb_method_call_with_block_kw(int argc, const VALUE *argv, VALUE recv, VALUE proc, int kw_splat);
+
+/**
+ * Queries the number of mandatory arguments of the method defined in the given
+ * module. If it is declared to take no arguments, returns `0`. If it takes
+ * exactly `n` arguments, returns `n`. If it has optional arguments, returns
+ * `-n-1`, where `n` is the number of mandatory arguments. Keyword arguments
+ * will be considered as a single additional argument, that argument being
+ * mandatory if any keyword argument is mandatory.
+ *
+ * @param[in] mod Namespace to search a method for.
+ * @param[in] mid Method id.
+ * @retval 0 It takes no arguments.
+ * @retval >0 It takes exactly this number of arguments.
+ * @retval <0 It takes optional arguments.
+ */
+int rb_mod_method_arity(VALUE mod, ID mid);
+
+/**
+ * Identical to rb_mod_method_arity(), except it searches for singleton methods
+ * rather than instance methods.
+ *
+ * @param[in] obj Object to search for a singleton method.
+ * @param[in] mid Method id.
+ * @retval 0 It takes no arguments.
+ * @retval >0 It takes exactly this number of arguments.
+ * @retval <0 It takes optional arguments.
+ */
+int rb_obj_method_arity(VALUE obj, ID mid);
+
+/* eval.c */
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Protects a function call from potential global escapes from the function.
+ * Such global escapes include exceptions, `throw`, `break`, for example.
+ *
+ * It first calls the function func with `args` as the argument. If no global
+ * escape occurred during the function, it returns the result and `*state` is
+ * zero. Otherwise, it returns ::RUBY_Qnil and sets `*state` to nonzero. If
+ * `state` is `NULL`, it is not set in both cases.
+ *
+ * @param[in] func A function that potentially escapes globally.
+ * @param[in] args Passed as-is to `func`.
+ * @param[out] state State of execution.
+ * @return What `func` returns, or an undefined value when it did not
+ * return.
+ * @post `*state` is set to zero if succeeded. Nonzero otherwise.
+ * @warning You have to clear the error info with `rb_set_errinfo(Qnil)` if
+ * you decide to ignore the caught exception.
+ * @see rb_eval_string_protect()
+ * @see rb_load_protect()
+ *
+ * @internal
+ *
+ * The "undefined value" described above is in fact ::RUBY_Qnil for now. But
+ * @shyouhei doesn't think that we would never change that.
+ *
+ * Though not a part of our public API, `state` is in fact an
+ * enum ruby_tag_type. You can see the potential "nonzero" values by looking
+ * at vm_core.h.
+ */
+VALUE rb_protect(VALUE (*func)(VALUE args), VALUE args, int *state);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_PROC_H */
diff --git a/include/ruby/internal/intern/process.h b/include/ruby/internal/intern/process.h
new file mode 100644
index 0000000000..cfa5e13162
--- /dev/null
+++ b/include/ruby/internal/intern/process.h
@@ -0,0 +1,282 @@
+#ifndef RBIMPL_INTERN_PROCESS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_PROCESS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_mProcess.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/config.h" /* rb_pid_t is defined here. */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* process.c */
+
+/**
+ * Wait for the specified process to terminate, reap it, and return its status.
+ *
+ * @param[in] pid The process ID to wait for.
+ * @param[in] flags The flags to pass to waitpid(2).
+ * @return VALUE An instance of Process::Status.
+ */
+VALUE rb_process_status_wait(rb_pid_t pid, int flags);
+
+/**
+ * Sets the "last status", or the `$?`.
+ *
+ * @param[in] status The termination status, as defined in `waitpid(3posix)`.
+ * @param[in] pid The last child of the current process.
+ * @post `$?` is updated.
+ */
+void rb_last_status_set(int status, rb_pid_t pid);
+
+/**
+ * Queries the "last status", or the `$?`.
+ *
+ * @retval RUBY_Qnil The current thread has no dead children.
+ * @retval otherwise An instance of Process::Status describing the status of
+ * the child that was most recently `wait`-ed.
+ */
+VALUE rb_last_status_get(void);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Executes a shell command.
+ *
+ * @warning THIS FUNCTION RETURNS on error!
+ * @param[in] cmd Passed to the shell.
+ * @retval -1 Something prevented the command execution.
+ * @post Upon successful execution this function doesn't return.
+ * @post In case it returns the `errno` is set properly.
+ */
+int rb_proc_exec(const char *cmd);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Replaces the current process by running the given external command. This is
+ * the implementation of `Kernel#exec`.
+ *
+ * @param[in] argc Number of objects in `argv`.
+ * @param[in] argv Command and its options to execute.
+ * @exception rb_eTypeError Invalid options e.g. non-String argv.
+ * @exception rb_eArgError Invalid options e.g. redirection cycle.
+ * @exception rb_eNotImpError Not implemented e.g. no `setuid(2)`.
+ * @exception rb_eRuntimeError `Process::UID.switch` in operation.
+ * @exception rb_eSystemCallError `execve(2)` failed.
+ * @warning This function doesn't return.
+ * @warning On failure it raises. On success the process is replaced.
+ *
+ * @internal
+ *
+ * @shyouhei have to say that the rdoc for `Kernel#exec` is fairly incomplete.
+ * AFAIK this function ultimately takes the following signature:
+ *
+ * ```rbs
+ * type boolx = bool | nil # != `boolish`
+ *
+ * type rlim_t = Integer # rlim_cur
+ * | [ Integer, Integer ] # rlim_cur, rlim_max
+ *
+ * type uid_t = String # e.g. "root"
+ * | Integer # e.g. 0
+ *
+ * type gid_t = String # e.g. "wheel"
+ * | Integer # e.g. 0
+ *
+ * type fmode = String # e.g. "rb"
+ * | Integer # e.g. O_RDONLY | O_BINARY
+ *
+ * type mode_t = Integer # e.g. 0644
+ *
+ * type pgrp = true # Creates a dedicated pgroup
+ * | 0 # ditto
+ * | nil # Uses the current one
+ * | Integer # Uses this specific pgroup
+ *
+ * type fd = :in # STDIN
+ * | :out # STDOUT
+ * | :err # STDERR
+ * | IO # This specific IO
+ * | Integer # A file descriptor of this #
+ *
+ * type src = fd | [ fd ]
+ * type dst = :close # Intuitive
+ * | fd # Intuitive
+ * | String # Open a file at this path
+ * | [ String ] # ... using O_RDONLY
+ * | [ String, fmode ] # ... using this mode
+ * | [ String, fmode, mode_t ] # ... with a permission
+ * | [ :child, fd ] # fd of child side
+ *
+ * type redir = Hash[ src, dst ]
+ *
+ * # ----
+ *
+ * # Key-value pair of environment variables
+ * type envp = Hash[ String, String ]
+ *
+ * # Actual name (and the name passed to the subprocess if any)
+ * type arg0 = String | [ String, String ]
+ *
+ * # Arbitrary string parameters
+ * type argv = String
+ *
+ * # Exec options:
+ * type argh = redir | {
+ * chdir: String, # Working directory
+ * close_others: boolx, # O_CLOEXEC like behaviour
+ * gid: gid_t, # setegid(2)
+ * pgrooup: pgrp, # setpgrp(2)
+ * rlimit_as: rlim_t, # setrlimit(2)
+ * rlimit_core: rlim_t, # ditto
+ * rlimit_cpu: rlim_t, # ditto
+ * rlimit_data: rlim_t, # ditto
+ * rlimit_fsize: rlim_t, # ditto
+ * rlimit_memlock: rlim_t, # ditto
+ * rlimit_msgqueue: rlim_t, # ditto
+ * rlimit_nice: rlim_t, # ditto
+ * rlimit_nofile: rlim_t, # ditto
+ * rlimit_nproc: rlim_t, # ditto
+ * rlimit_rss: rlim_t, # ditto
+ * rlimit_rtprio: rlim_t, # ditto
+ * rlimit_rttime: rlim_t, # ditto
+ * rlimit_sbsize: rlim_t, # ditto
+ * rlimit_sigpending: rlim_t, # ditto
+ * rlimit_stack: rlim_t, # ditto
+ * uid: uid_t, # seteuid(2)
+ * umask: mode_t, # umask(2)
+ * unsetenv_others: boolx # Unset everything except the passed envp
+ * }
+ *
+ * # ====
+ *
+ * class Kernel
+ * def self?.exec
+ * : ( arg0 cmd, *argv args ) -> void
+ * | ( arg0 cmd, *argv args, argh opts) -> void
+ * | (envp env, arg0 cmd, *argv args ) -> void
+ * | (envp env, arg0 cmd, *argv args, argh opts) -> void
+ * end
+ * ```
+ */
+VALUE rb_f_exec(int argc, const VALUE *argv);
+
+/**
+ * Waits for a process, with releasing GVL.
+ *
+ * @param[in] pid Process ID.
+ * @param[out] status The wait status is filled back.
+ * @param[in] flags Wait options.
+ * @retval -1 System call failed, errno set.
+ * @retval 0 WNOHANG but no waitable children.
+ * @retval otherwise A process ID that was `wait()`-ed.
+ * @post Upon successful return `status` is updated to have the process'
+ * status.
+ * @note `status` can be NULL.
+ * @note The arguments are passed through to underlying system call(s).
+ * Can have special meanings. For instance passing `(rb_pid_t)-1`
+ * to `pid` means it waits for any processes, under
+ * POSIX-compliant situations.
+ */
+rb_pid_t rb_waitpid(rb_pid_t pid, int *status, int flags);
+
+/**
+ * This is a shorthand of rb_waitpid without status and flags. It has been
+ * like this since the very beginning. The initial revision already did the
+ * same thing. Not sure why, then, it has been named `syswait`. AFAIK this is
+ * different from how `wait(3posix)` works.
+ *
+ * @param[in] pid Passed to rb_waitpid().
+ */
+void rb_syswait(rb_pid_t pid);
+
+/**
+ * Identical to rb_f_exec(), except it spawns a child process instead of
+ * replacing the current one.
+ *
+ * @param[in] argc Number of objects in `argv`.
+ * @param[in] argv Command and its options to execute.
+ * @exception rb_eTypeError Invalid options e.g. non-String argv.
+ * @exception rb_eArgError Invalid options e.g. redirection cycle.
+ * @exception rb_eNotImpError Not implemented e.g. no `setuid(2)`.
+ * @exception rb_eRuntimeError `Process::UID.switch` in operation.
+ * @retval -1 Child process died for some reason.
+ * @retval otherwise The ID of the born child.
+ *
+ * @internal
+ *
+ * This is _really_ identical to rb_f_exec() until ultimately calling the
+ * system call. Almost everything are shared among these two (and
+ * rb_f_system()).
+ */
+rb_pid_t rb_spawn(int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_spawn(), except you can additionally know the detailed
+ * situation in case of abnormal parturitions.
+ *
+ * @param[in] argc Number of objects in `argv`.
+ * @param[in] argv Command and its options to execute.
+ * @param[out] errbuf Error description write-back buffer.
+ * @param[in] buflen Number of bytes of `errbuf`, including NUL.
+ * @exception rb_eTypeError Invalid options e.g. non-String argv.
+ * @exception rb_eArgError Invalid options e.g. redirection cycle.
+ * @exception rb_eNotImpError Not implemented e.g. no `setuid(2)`.
+ * @exception rb_eRuntimeError `Process::UID.switch` in operation.
+ * @retval -1 Child process died for some reason.
+ * @retval otherwise The ID of the born child.
+ * @post In case of `-1`, at most `buflen` bytes of the reason why is
+ * written back to `errbuf`.
+ */
+rb_pid_t rb_spawn_err(int argc, const VALUE *argv, char *errbuf, size_t buflen);
+
+/**
+ * Gathers info about resources consumed by the current process.
+ *
+ * @param[in] _ Not used. Pass anything.
+ * @return An instance of `Process::Tms`.
+ *
+ * @internal
+ *
+ * This function might or might not exist depending on `./configure` result.
+ * It must be a portability hell. Better not use.
+ */
+VALUE rb_proc_times(VALUE _);
+
+/**
+ * "Detaches" a subprocess. In POSIX systems every child processes that a
+ * process creates must be `wait(2)`-ed. A child process that died yet has not
+ * been waited so far is called a "zombie", which more or less consumes
+ * resources. This function automates reclamation of such processes. Once
+ * after this function successfully returns you can basically forget about the
+ * child process.
+ *
+ * @param[in] pid Process to wait.
+ * @return An instance of ::rb_cThread which is `waitpid(2)`-ing `pid`.
+ * @post You can just forget about the return value. GC reclaims it.
+ * @post You can know the exit status by querying `#value` of the
+ * return value (which is a blocking operation).
+ */
+VALUE rb_detach_process(rb_pid_t pid);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_PROCESS_H */
diff --git a/include/ruby/internal/intern/random.h b/include/ruby/internal/intern/random.h
new file mode 100644
index 0000000000..5577f53cb4
--- /dev/null
+++ b/include/ruby/internal/intern/random.h
@@ -0,0 +1,116 @@
+#ifndef RBIMPL_INTERN_RANDOM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_RANDOM_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief MT19937 backended pseudo random number generator.
+ * @see Matsumoto, M., Nishimura, T., "Mersenne Twister: A 623-
+ * dimensionally equidistributed uniform pseudorandom number
+ * generator", ACM Trans. on Modeling and Computer Simulation, 8
+ * (1): pp 3-30, 1998. https://doi.org/10.1145/272991.272995
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* random.c */
+
+/**
+ * Generates a 32 bit random number.
+ *
+ * @return A random number.
+ * @note Now that we have ractors, the RNG behind this function is
+ * per-ractor.
+ */
+unsigned int rb_genrand_int32(void);
+
+/**
+ * Generates a `double` random number.
+ *
+ * @return A random number.
+ * @note This function shares the RNG with rb_genrand_int32().
+ */
+double rb_genrand_real(void);
+
+/**
+ * Resets the RNG behind rb_genrand_int32()/rb_genrand_real().
+ *
+ * @post The (now per-ractor) default RNG's internal state is cleared.
+ */
+void rb_reset_random_seed(void);
+
+/**
+ * Generates a String of random bytes.
+ *
+ * @param[in,out] rnd An instance of ::rb_cRandom.
+ * @param[in] n Requested number of bytes.
+ * @return An instance of ::rb_cString, of binary, of `n` bytes length,
+ * whose contents are random bits.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't know if this is an Easter egg or an official feature, but
+ * this function can take a wider range of objects, such as `Socket::Ifaddr`.
+ * The arguments are just silently ignored and the default RNG is used instead,
+ * if they are non-RNG.
+ */
+VALUE rb_random_bytes(VALUE rnd, long n);
+
+/**
+ * Identical to rb_genrand_int32(), except it generates using the passed RNG.
+ *
+ * @param[in,out] rnd An instance of ::rb_cRandom.
+ * @return A random number.
+ */
+unsigned int rb_random_int32(VALUE rnd);
+
+/**
+ * Identical to rb_genrand_real(), except it generates using the passed RNG.
+ *
+ * @param[in,out] rnd An instance of ::rb_cRandom.
+ * @return A random number.
+ */
+double rb_random_real(VALUE rnd);
+
+/**
+ * Identical to rb_genrand_ulong_limited(), except it generates using the
+ * passed RNG.
+ *
+ * @param[in,out] rnd An instance of ::rb_cRandom.
+ * @param[in] limit Max possible return value.
+ * @return A random number, distributed in `[0, limit]` interval.
+ * @note Note it can return `limit`.
+ * @note Whether the return value distributes uniformly in the
+ * interval or not depends on how the argument RNG behaves; at
+ * least in case of MT19937 it does.
+ */
+unsigned long rb_random_ulong_limited(VALUE rnd, unsigned long limit);
+
+/**
+ * Generates a random number whose upper limit is `i`.
+ *
+ * @param[in] i Max possible return value.
+ * @return A random number, uniformly distributed in `[0, limit]` interval.
+ * @note Note it can return `i`.
+ */
+unsigned long rb_genrand_ulong_limited(unsigned long i);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_RANDOM_H */
diff --git a/include/ruby/internal/intern/range.h b/include/ruby/internal/intern/range.h
new file mode 100644
index 0000000000..1f7d7c313f
--- /dev/null
+++ b/include/ruby/internal/intern/range.h
@@ -0,0 +1,89 @@
+#ifndef RBIMPL_INTERN_RANGE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_RANGE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cRange.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* range.c */
+
+/**
+ * Creates a new Range.
+ *
+ * @param[in] beg "Left" or "lowest" endpoint of the range.
+ * @param[in] end "Right" or "highest" endpoint of the range.
+ * @param[in] excl Whether the range is open-ended.
+ * @exception rb_eArgError `beg` and `end` are not comparable.
+ * @note These days both endpoints can be ::RUBY_Qnil, which means that
+ * endpoint is unbound.
+ */
+VALUE rb_range_new(VALUE beg, VALUE end, int excl);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Deconstructs a numerical range. As the arguments are `long` based, it
+ * expects everything are in the `long` domain.
+ *
+ * @param[in] range A range of numerical endpoints.
+ * @param[out] begp Return value buffer.
+ * @param[out] lenp Return value buffer.
+ * @param[in] len Updated length.
+ * @param[in] err In case `len` is out of range...
+ * - `0`: returns ::RUBY_Qnil.
+ * - `1`: raises ::rb_eRangeError.
+ * - `2`: `beg` and `len` expanded accordingly.
+ * @exception rb_eTypeError `range` is not a numerical range.
+ * @exception rb_eRangeError `range` cannot fit into `long`.
+ * @retval RUBY_Qfalse `range` is not an ::rb_cRange.
+ * @retval RUBY_Qnil `len` is out of `range` but `err` is zero.
+ * @retval RUBY_Qtrue Otherwise.
+ * @post `beg` is the (possibly updated) left endpoint.
+ * @post `len` is the (possibly updated) length of the range.
+ *
+ * @internal
+ *
+ * The complex error handling switch reflects the fact that `Array#[]=` and
+ * `String#[]=` behave differently when they take ranges.
+ */
+VALUE rb_range_beg_len(VALUE range, long *begp, long *lenp, long len, int err);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Deconstructs a range into its components.
+ *
+ * @param[in] range Range or range-ish object.
+ * @param[out] begp Return value buffer.
+ * @param[out] endp Return value buffer.
+ * @param[out] exclp Return value buffer.
+ * @retval RUBY_Qfalse `range` is not an instance of ::rb_cRange.
+ * @retval RUBY_Qtrue Argument pointers are updated.
+ * @post `*begp` is the left endpoint of the range.
+ * @post `*endp` is the right endpoint of the range.
+ * @post `*exclp` is whether the range is open-ended or not.
+ */
+int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_RANGE_H */
diff --git a/include/ruby/internal/intern/rational.h b/include/ruby/internal/intern/rational.h
new file mode 100644
index 0000000000..ff4beca297
--- /dev/null
+++ b/include/ruby/internal/intern/rational.h
@@ -0,0 +1,172 @@
+#ifndef RBIMPL_INTERN_RATIONAL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_RATIONAL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cRational.
+ */
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/arithmetic/long.h" /* INT2FIX is here. */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* rational.c */
+
+/**
+ * Identical to rb_rational_new(), except it skips argument validations. It is
+ * thus dangerous for extension libraries. For instance `1/0r` could be
+ * constructed using this.
+ *
+ * @param[in] num Numerator, an instance of ::rb_cInteger.
+ * @param[in] den Denominator, an instance of ::rb_cInteger.
+ * @exception rb_eTypeError Either argument is not an Integer.
+ * @return An instance of ::rb_cRational whose value is `(num/den)r`.
+ */
+VALUE rb_rational_raw(VALUE num, VALUE den);
+
+/**
+ * Shorthand of `(x/1)r`. As `x` is already an Integer, it practically
+ * converts it into a Rational of the identical value.
+ *
+ * @param[in] x An instance of ::rb_cInteger.
+ * @return An instance of ::rb_cRational, whose value is `(x/1)r`.
+ */
+#define rb_rational_raw1(x) rb_rational_raw((x), INT2FIX(1))
+
+/** @alias{rb_rational_raw} */
+#define rb_rational_raw2(x,y) rb_rational_raw((x), (y))
+
+/**
+ * Constructs a Rational, with reduction. This returns for instance `(2/3)r`
+ * for `rb_rational_new(INT2NUM(-384), INT2NUM(-576))`.
+ *
+ * @param[in] num Numerator, an instance of ::rb_cInteger.
+ * @param[in] den Denominator, an instance of ::rb_cInteger.
+ * @exception rb_eZeroDivError `den` is zero.
+ * @return An instance of ::rb_cRational whose value is `(num/den)r`.
+ */
+VALUE rb_rational_new(VALUE num, VALUE den);
+
+/**
+ * Shorthand of `(x/1)r`. As `x` is already an Integer, it practically
+ * converts it into a Rational of the identical value.
+ *
+ * @param[in] x An instance of ::rb_cInteger.
+ * @return An instance of ::rb_cRational, whose value is `(x/1)r`.
+ */
+#define rb_rational_new1(x) rb_rational_new((x), INT2FIX(1))
+
+/** @alias{rb_rational_new} */
+#define rb_rational_new2(x,y) rb_rational_new((x), (y))
+
+/**
+ * Converts various values into a Rational. This function accepts:
+ *
+ * - Instances of ::rb_cInteger (taken as-is),
+ * - Instances of ::rb_cRational (taken as-is),
+ * - Instances of ::rb_cFloat (applies `#to_r`),
+ * - Instances of ::rb_cComplex (applies `#to_r`),
+ * - Instances of ::rb_cString (applies `#to_r`),
+ * - Other objects that respond to `#to_r`.
+ *
+ * It (possibly recursively) applies `#to_r` until both sides become either
+ * Integer or Rational, then divides them.
+ *
+ * As a special case, passing ::RUBY_Qundef to `den` is the same as passing
+ * `RB_INT2NUM(1)`.
+ *
+ * @param[in] num Numerator (see above).
+ * @param[in] den Denominator (see above).
+ * @exception rb_eTypeError Passed something not described above.
+ * @exception rb_eFloatDomainError `#to_r` produced Nan/Inf.
+ * @exception rb_eZeroDivError `#to_r` produced zero for `den`.
+ * @return An instance of ::rb_cRational whose value is `(num/den)r`.
+ *
+ * @internal
+ *
+ * This was the implementation of `Kernel#Rational` before, but they diverged.
+ */
+VALUE rb_Rational(VALUE num, VALUE den);
+
+/**
+ * Shorthand of `(x/1)r`. It practically converts it into a Rational of the
+ * identical value.
+ *
+ * @param[in] x ::rb_cInteger, ::rb_cRational, or something that responds to
+ * `#to_r`.
+ * @return An instance of ::rb_cRational, whose value is `(x/1)r`.
+ */
+#define rb_Rational1(x) rb_Rational((x), INT2FIX(1))
+
+/** @alias{rb_Rational} */
+#define rb_Rational2(x,y) rb_Rational((x), (y))
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries the numerator of the passed Rational.
+ *
+ * @param[in] rat An instance of ::rb_cRational.
+ * @return Its numerator part, which is an instance of ::rb_cInteger.
+ */
+VALUE rb_rational_num(VALUE rat);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries the denominator of the passed Rational.
+ *
+ * @param[in] rat An instance of ::rb_cRational.
+ * @return Its denominator part, which is an instance of ::rb_cInteger
+ * greater than or equal to one..
+ */
+VALUE rb_rational_den(VALUE rat);
+
+/**
+ * Simplified approximation of a float. It returns a rational `rat` which
+ * satisfies:
+ *
+ * ```
+ * flt - |prec| <= rat <= flt + |prec|
+ * ```
+ *
+ * ```ruby
+ * 3.141592.rationalize(0.001) # => (201/64)r
+ * 3.141592.rationalize(0.01)' # => (22/7)r
+ * 3.141592.rationalize(0.1)' # => (16/5)r
+ * 3.141592.rationalize(1)' # => (3/1)r
+ * ```
+ *
+ * @param[in] flt An instance of ::rb_cFloat to rationalise.
+ * @param[in] prec Another ::rb_cFloat, which is the "precision".
+ * @return Approximation of `flt`, in ::rb_cRational.
+ */
+VALUE rb_flt_rationalize_with_prec(VALUE flt, VALUE prec);
+
+/**
+ * Identical to rb_flt_rationalize_with_prec(), except it auto-detects
+ * appropriate precision depending on the passed value.
+ *
+ * @param[in] flt An instance of ::rb_cFloat to rationalise.
+ * @return Approximation of `flt`, in ::rb_cRational.
+ */
+VALUE rb_flt_rationalize(VALUE flt);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_RATIONAL_H */
diff --git a/include/ruby/internal/intern/re.h b/include/ruby/internal/intern/re.h
new file mode 100644
index 0000000000..4dd58b469b
--- /dev/null
+++ b/include/ruby/internal/intern/re.h
@@ -0,0 +1,244 @@
+#ifndef RBIMPL_INTERN_RE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_RE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cRegexp.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* re.c */
+
+/**
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ *
+ * @internal
+ *
+ * This was a function that switched between memcmp and rb_memcicmp depending
+ * on then-called `ruby_ignorecase`, or the `$=` global variable. That feature
+ * was abandoned in sometime around version 1.9.0.
+ */
+#define rb_memcmp memcmp
+
+/**
+ * Identical to st_locale_insensitive_strcasecmp(), except it is timing safe
+ * and returns something different.
+ *
+ * @param[in] s1 Comparison LHS.
+ * @param[in] s2 Comparison RHS.
+ * @param[in] n Comparison shall stop after first `n` bytes are scanned.
+ * @retval <0 `s1` is "less" than `s2`.
+ * @retval 0 Both sides converted into lowercase would be identical.
+ * @retval >0 `s1` is "greater" than `s2`.
+ * @note The "case" here means that of the POSIX Locale.
+ *
+ * @internal
+ *
+ * Can accept NULLs as long as n is also 0, and returns 0.
+ */
+int rb_memcicmp(const void *s1,const void *s2, long n);
+
+/**
+ * Asserts that the given MatchData is "occupied". MatchData shares its
+ * backend storages with its Regexp object. But programs can destructively
+ * tamper its contents. Calling this function beforehand shall prevent such
+ * modifications to spill over into other objects.
+ *
+ * @param[out] md Target instance of ::rb_cMatch.
+ * @post The object is "busy".
+ *
+ * @internal
+ *
+ * There is rb_match_unbusy internally, but extension libraries are left unable
+ * to do so.
+ */
+void rb_match_busy(VALUE md);
+
+/**
+ * Identical to rb_reg_nth_match(), except it just returns Boolean. This could
+ * skip allocating a returning string, resulting in reduced memory footprints
+ * if applicable.
+ *
+ * @param[in] n Match index.
+ * @param[in] md An instance of ::rb_cMatch.
+ * @exception rb_eTypeError `md` is not initialised.
+ * @retval RUBY_Qnil There is no `n`-th capture.
+ * @retval RUBY_Qfalse There is a `n`-th capture and is empty.
+ * @retval RUBY_Qtrue There is a `n`-th capture that has something.
+ *
+ */
+VALUE rb_reg_nth_defined(int n, VALUE md);
+
+/**
+ * Queries the nth captured substring.
+ *
+ * @param[in] n Match index.
+ * @param[in] md An instance of ::rb_cMatch.
+ * @exception rb_eTypeError `md` is not initialised.
+ * @retval RUBY_Qnil There is no `n`-th capture.
+ * @retval otherwise An allocated instance of ::rb_cString containing
+ * the contents captured.
+ */
+VALUE rb_reg_nth_match(int n, VALUE md);
+
+/**
+ * Queries the index of the given named capture. Captures could be named. But
+ * that doesn't mean named ones are not indexed. A regular expression can mix
+ * named and non-named captures, and they are all indexed. This function
+ * converts from a name to its index.
+ *
+ * @param[in] match An instance of ::rb_cMatch.
+ * @param[in] backref Capture name, in String, Symbol, or Numeric.
+ * @exception rb_eIndexError No such named capture.
+ * @return The index of the given name.
+ */
+int rb_reg_backref_number(VALUE match, VALUE backref);
+
+/**
+ * This just returns the argument, stringified. What a poor name.
+ *
+ * @param[in] md An instance of ::rb_cMatch.
+ * @return Its 0th capture (i.e. entire matched string).
+ */
+VALUE rb_reg_last_match(VALUE md);
+
+/**
+ * The portion of the original string before the given match.
+ *
+ * @param[in] md An instance of ::rb_cMatch.
+ * @return Its "prematch". This is perl's ``$```.
+ */
+VALUE rb_reg_match_pre(VALUE md);
+
+/**
+ * The portion of the original string after the given match.
+ *
+ * @param[in] md An instance of ::rb_cMatch.
+ * @return Its "postmatch". This is perl's `$'`.
+ */
+VALUE rb_reg_match_post(VALUE md);
+
+/**
+ * The portion of the original string that captured at the very last.
+ *
+ * @param[in] md An instance of ::rb_cMatch.
+ * @return Its "lastmatch". This is perl's `$+`.
+ */
+VALUE rb_reg_match_last(VALUE md);
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define HAVE_RB_REG_NEW_STR 1
+
+/**
+ * Identical to rb_reg_new(), except it takes the expression in Ruby's string
+ * instead of C's.
+ *
+ * @param[in] src Source code in String.
+ * @param[in] opts Options e.g. ONIG_OPTION_MULTILINE.
+ * @exception rb_eRegexpError `src` and `opts` do not interface.
+ * @return Allocated new instance of ::rb_cRegexp.
+ */
+VALUE rb_reg_new_str(VALUE src, int opts);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Creates a new Regular expression.
+ *
+ * @param[in] src Source code.
+ * @param[in] len `strlen(src)`.
+ * @param[in] opts Options e.g. ONIG_OPTION_MULTILINE.
+ * @return Allocated new instance of ::rb_cRegexp.
+ */
+VALUE rb_reg_new(const char *src, long len, int opts);
+
+/**
+ * Allocates an instance of ::rb_cRegexp.
+ *
+ * @private
+ *
+ * Nobody should call this function. Regular expressions that are not
+ * initialised must not exist in the wild.
+ */
+VALUE rb_reg_alloc(void);
+
+/**
+ * Initialises an instance of ::rb_cRegexp.
+ *
+ * @private
+ *
+ * This just raises for ordinal regexp objects. Extension libraries must not
+ * use.
+ */
+VALUE rb_reg_init_str(VALUE re, VALUE s, int options);
+
+/**
+ * This is the match operator.
+ *
+ * @param[in] re An instance of ::rb_cRegexp.
+ * @param[in] str An instance of ::rb_cString.
+ * @exception rb_eTypeError `str` is not a string.
+ * @exception rb_eRegexpError Error inside of Onigmo (unlikely).
+ * @retval RUBY_Qnil Match failed.
+ * @retval otherwise Matched position (character index inside of
+ * `str`).
+ * @post `Regexp.last_match` is updated.
+ * @post `$&`, `$~`, etc., are updated.
+ * @note If you do this in ruby, named captures are assigned to local
+ * variable of the local scope. But that doesn't happen here. The
+ * assignment is done by the interpreter.
+ */
+VALUE rb_reg_match(VALUE re, VALUE str);
+
+/**
+ * Identical to rb_reg_match(), except it matches against rb_lastline_get()
+ * (or, the `$_`).
+ *
+ * @param[in] re An instance of ::rb_cRegexp.
+ * @exception rb_eRegexpError Error inside of Onigmo (unlikely).
+ * @retval RUBY_Qnil Match failed or `$_` is absent.
+ * @retval otherwise Matched position (character index inside of
+ * `$_`).
+ * @post `Regexp.last_match` is updated.
+ * @post `$&`, `$~`, etc., are updated.
+ */
+VALUE rb_reg_match2(VALUE re);
+
+/**
+ * Queries the options of the passed regular expression.
+ *
+ * @param[in] re An instance of ::rb_cRegexp.
+ * @return Its options.
+ * @note Possible return values are defined in Onigmo.h.
+ */
+int rb_reg_options(VALUE re);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_RE_H */
diff --git a/include/ruby/internal/intern/ruby.h b/include/ruby/internal/intern/ruby.h
new file mode 100644
index 0000000000..efe61424ca
--- /dev/null
+++ b/include/ruby/internal/intern/ruby.h
@@ -0,0 +1,77 @@
+#ifndef RBIMPL_INTERN_RUBY_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_RUBY_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Process-global APIs.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* ruby.c */
+/** @alias{rb_get_argv} */
+#define rb_argv rb_get_argv()
+
+/**
+ * The value of `$0` at process bootup.
+ *
+ * @note This is just a snapshot of `$0`, not the backend storage of it. `$0`
+ * could become something different because it is a writable global
+ * variable. Modifying it for instance affects `ps(1)` output. Don't
+ * assume they are synced.
+ */
+RUBY_EXTERN VALUE rb_argv0;
+
+/* io.c */
+
+/**
+ * Queries the arguments passed to the current process that you can access from
+ * Ruby as `ARGV`.
+ *
+ * @return An array of strings containing arguments passed to the process.
+ */
+VALUE rb_get_argv(void);
+
+/* ruby.c */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Loads the given file. This function opens the given pathname for reading,
+ * parses the contents as a Ruby script, and returns an opaque "node" pointer.
+ * You can then pass it to ruby_run_node() for evaluation.
+ *
+ * @param[in] file File name, or "-" to read from stdin.
+ * @return Opaque "node" pointer.
+ */
+void *rb_load_file(const char *file);
+
+/**
+ * Identical to rb_load_file(), except it takes the argument as a Ruby's string
+ * instead of C's.
+ *
+ * @param[in] file File name, or "-" to read from stdin.
+ * @return Opaque "node" pointer.
+ */
+void *rb_load_file_str(VALUE file);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_RUBY_H */
diff --git a/include/ruby/internal/intern/select.h b/include/ruby/internal/intern/select.h
new file mode 100644
index 0000000000..ba75213618
--- /dev/null
+++ b/include/ruby/internal/intern/select.h
@@ -0,0 +1,88 @@
+#ifndef RBIMPL_INTERN_SELECT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_SELECT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs to provide ::rb_fd_select().
+ * @note Functions and structs defined in this header file are not
+ * necessarily ruby-specific. They don't need ::VALUE etc.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h> /* for NFDBITS (BSD Net/2) */
+#endif
+
+#include "ruby/internal/dllexport.h"
+
+/* thread.c */
+#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
+# include "ruby/internal/intern/select/largesize.h"
+#elif defined(_WIN32)
+# include "ruby/internal/intern/select/win32.h"
+# /** Does nothing (defined for compatibility). */
+# define rb_fd_resize(n, f) ((void)(f))
+#else
+# include "ruby/internal/intern/select/posix.h"
+# /** Does nothing (defined for compatibility). */
+# define rb_fd_resize(n, f) ((void)(f))
+#endif
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+struct timeval;
+
+/**
+ * Waits for multiple file descriptors at once. This is basically a wrapper of
+ * system-provided select() with releasing GVL, to allow other Ruby threads run
+ * in parallel.
+ *
+ * @param[in] nfds Max FD in everything passed, plus one.
+ * @param[in,out] rfds Set of FDs to wait for reads.
+ * @param[in,out] wfds Set of FDs to wait for writes.
+ * @param[in,out] efds Set of FDs to wait for OOBs.
+ * @param[in,out] timeout Max blocking duration.
+ * @retval -1 Failed, errno set.
+ * @retval 0 Timeout exceeded.
+ * @retval otherwise Total number of file descriptors returned.
+ * @post `rfds` contains readable FDs.
+ * @post `wfds` contains writable FDs.
+ * @post `efds` contains exceptional FDs.
+ * @post `timeout` is the time left.
+ * @note All pointers are allowed to be null pointers.
+ *
+ * Although backend threads can run in parallel of this function, touching a
+ * file descriptor from multiple threads could be problematic. For instance
+ * what happens when a thread closes a file descriptor that is selected by
+ * someone else, vastly varies among operating systems. You would better avoid
+ * touching an fd from more than one threads.
+ *
+ * NOTE: this function is used in native extensions, so change its API with care.
+ *
+ * @internal
+ *
+ * Although any file descriptors are possible here, it makes completely no
+ * sense to pass a descriptor that is not `O_NONBLOCK`. If you want to know
+ * the reason for this limitation in detail, you might find this thread super
+ * interesting: https://lkml.org/lkml/2004/10/6/117
+ */
+int rb_thread_fd_select(int nfds, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_SELECT_H */
diff --git a/include/ruby/internal/intern/select/largesize.h b/include/ruby/internal/intern/select/largesize.h
new file mode 100644
index 0000000000..d65f088c06
--- /dev/null
+++ b/include/ruby/internal/intern/select/largesize.h
@@ -0,0 +1,214 @@
+#ifndef RBIMPL_INTERN_SELECT_LARGESIZE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_SELECT_LARGESIZE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs to provide ::rb_fd_select().
+ *
+ * Several Unix platforms support file descriptors bigger than FD_SETSIZE in
+ * `select(2)` system call.
+ *
+ * - Linux 2.2.12 (?)
+ *
+ * - NetBSD 1.2 (src/sys/kern/sys_generic.c:1.25)
+ * `select(2)` documents how to allocate fd_set dynamically.
+ * http://netbsd.gw.com/cgi-bin/man-cgi?select++NetBSD-4.0
+ *
+ * - FreeBSD 2.2 (src/sys/kern/sys_generic.c:1.19)
+ *
+ * - OpenBSD 2.0 (src/sys/kern/sys_generic.c:1.4)
+ * `select(2)` documents how to allocate fd_set dynamically.
+ * http://www.openbsd.org/cgi-bin/man.cgi?query=select&manpath=OpenBSD+4.4
+ *
+ * - Solaris 8 has `select_large_fdset`
+ *
+ * - Mac OS X 10.7 (Lion)
+ * `select(2)` returns `EINVAL` if `nfds` is greater than `FD_SET_SIZE` and
+ * `_DARWIN_UNLIMITED_SELECT` (or `_DARWIN_C_SOURCE`) isn't defined.
+ * http://developer.apple.com/library/mac/#releasenotes/Darwin/SymbolVariantsRelNotes/_index.html
+ *
+ * When `fd_set` is not big enough to hold big file descriptors, it should be
+ * allocated dynamically. Note that this assumes `fd_set` is structured as
+ * bitmap.
+ *
+ * `rb_fd_init` allocates the memory.
+ * `rb_fd_term` frees the memory.
+ * `rb_fd_set` may re-allocate bitmap.
+ *
+ * So `rb_fd_set` doesn't reject file descriptors bigger than `FD_SETSIZE`.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+
+/**@cond INTERNAL_MACRO */
+#define rb_fd_ptr rb_fd_ptr
+#define rb_fd_max rb_fd_max
+/** @endcond */
+
+struct timeval;
+
+/**
+ * The data structure which wraps the fd_set bitmap used by select(2). This
+ * allows Ruby to use FD sets larger than that allowed by historic limitations
+ * on modern platforms.
+ */
+typedef struct {
+ int maxfd; /**< Maximum allowed number of FDs. */
+ fd_set *fdset; /**< File descriptors buffer */
+} rb_fdset_t;
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * (Re-)initialises a fdset. One must be initialised before other `rb_fd_*`
+ * operations. Analogous to calling `malloc(3)` to allocate an `fd_set`.
+ *
+ * @param[out] f An fdset to squash.
+ * @post `f` holds no file descriptors.
+ */
+void rb_fd_init(rb_fdset_t *f);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Destroys the ::rb_fdset_t, releasing any memory and resources it used. It
+ * must be reinitialised using rb_fd_init() before future use. Analogous to
+ * calling `free(3)` to release memory for an `fd_set`.
+ *
+ * @param[out] f An fdset to squash.
+ * @post `f` holds no file descriptors.
+ */
+void rb_fd_term(rb_fdset_t *f);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Wipes out the current set of FDs.
+ *
+ * @param[out] f The fdset to clear.
+ * @post `f` has no FDs.
+ */
+void rb_fd_zero(rb_fdset_t *f);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Sets an fd to a fdset.
+ *
+ * @param[in] fd A file descriptor.
+ * @param[out] f Target fdset.
+ * @post `f` holds `fd`.
+ */
+void rb_fd_set(int fd, rb_fdset_t *f);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Releases a specific FD from the given fdset.
+ *
+ * @param[in] fd Target FD.
+ * @param[out] f The fdset that holds `fd`.
+ * @post `f` doesn't hold n.
+ */
+void rb_fd_clr(int fd, rb_fdset_t *f);
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_PURE()
+/**
+ * Queries if the given FD is in the given set.
+ *
+ * @param[in] fd Target FD.
+ * @param[in] f The fdset to scan.
+ * @retval 1 Yes there is.
+ * @retval 0 No there isn't.
+ * @see http://www.freebsd.org/cgi/query-pr.cgi?pr=91421
+ */
+int rb_fd_isset(int fd, const rb_fdset_t *f);
+
+/**
+ * Destructively overwrites an fdset with another.
+ *
+ * @param[out] dst Target fdset.
+ * @param[in] src Source fdset.
+ * @param[in] max Maximum number of file descriptors to copy.
+ * @post `dst` is a copy of `src`.
+ */
+void rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max);
+
+/**
+ * Identical to rb_fd_copy(), except it copies unlimited number of file
+ * descriptors.
+ *
+ * @param[out] dst Target fdset.
+ * @param[in] src Source fdset.
+ * @post `dst` is a copy of `src`.
+ */
+void rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src);
+
+/**
+ * Waits for multiple file descriptors at once.
+ *
+ * @param[in] nfds Max FD in everything passed, plus one.
+ * @param[in,out] rfds Set of FDs to wait for reads.
+ * @param[in,out] wfds Set of FDs to wait for writes.
+ * @param[in,out] efds Set of FDs to wait for OOBs.
+ * @param[in,out] timeout Max blocking duration.
+ * @retval -1 Failed, errno set.
+ * @retval 0 Timeout exceeded.
+ * @retval otherwise Total number of file descriptors returned.
+ * @post `rfds` contains readable FDs.
+ * @post `wfds` contains writable FDs.
+ * @post `efds` contains exceptional FDs.
+ * @post `timeout` is the time left.
+ * @note All pointers are allowed to be null pointers.
+ */
+int rb_fd_select(int nfds, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout);
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_PURE()
+/**
+ * Raw pointer to `fd_set`.
+ *
+ * @param[in] f Target fdset.
+ * @retval NULL `f` is already terminated by rb_fd_term().
+ * @retval otherwise Underlying fd_set.
+ *
+ * @internal
+ *
+ * Extension library must not touch raw pointers. It was a bad idea to let
+ * them use it.
+ */
+static inline fd_set *
+rb_fd_ptr(const rb_fdset_t *f)
+{
+ return f->fdset;
+}
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_PURE()
+/**
+ * It seems this function has no use. Maybe just remove?
+ *
+ * @param[in] f A set.
+ * @return Number of file descriptors stored.
+ */
+static inline int
+rb_fd_max(const rb_fdset_t *f)
+{
+ return f->maxfd;
+}
+
+#endif /* RBIMPL_INTERN_SELECT_LARGESIZE_H */
diff --git a/include/ruby/internal/intern/select/posix.h b/include/ruby/internal/intern/select/posix.h
new file mode 100644
index 0000000000..0a9b0b2e51
--- /dev/null
+++ b/include/ruby/internal/intern/select/posix.h
@@ -0,0 +1,144 @@
+#ifndef RBIMPL_INTERN_SELECT_POSIX_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_SELECT_POSIX_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs to provide ::rb_fd_select().
+ */
+#include "ruby/internal/config.h"
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h> /* for select(2) (modern POSIX) */
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> /* for select(2) (archaic UNIX) */
+#endif
+
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+
+/**
+ * The data structure which wraps the fd_set bitmap used by `select(2)`. This
+ * allows Ruby to use FD sets larger than what has been historically allowed on
+ * modern platforms.
+ *
+ * @internal
+ *
+ * ... but because this header file is included only when the system is with
+ * that "historic restrictions", this is nothing more than an alias of fd_set.
+ */
+typedef fd_set rb_fdset_t;
+
+/** Clears the given ::rb_fdset_t. */
+#define rb_fd_zero FD_ZERO
+
+/** Sets the given fd to the ::rb_fdset_t. */
+#define rb_fd_set FD_SET
+
+/** Unsets the given fd from the ::rb_fdset_t. */
+#define rb_fd_clr FD_CLR
+
+/** Queries if the given fd is in the ::rb_fdset_t. */
+#define rb_fd_isset FD_ISSET
+
+/** Initialises the :given :rb_fdset_t. */
+#define rb_fd_init FD_ZERO
+
+/** Waits for multiple file descriptors at once. */
+#define rb_fd_select select
+
+/**@cond INTERNAL_MACRO */
+#define rb_fd_copy rb_fd_copy
+#define rb_fd_dup rb_fd_dup
+#define rb_fd_ptr rb_fd_ptr
+#define rb_fd_max rb_fd_max
+/** @endcond */
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_NOALIAS()
+/**
+ * Destructively overwrites an fdset with another.
+ *
+ * @param[out] dst Target fdset.
+ * @param[in] src Source fdset.
+ * @param[in] n Unused parameter.
+ * @post `dst` is a copy of `src`.
+ */
+static inline void
+rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int n)
+{
+ *dst = *src;
+}
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_NOALIAS()
+/**
+ * Destructively overwrites an fdset with another.
+ *
+ * @param[out] dst Target fdset.
+ * @param[in] src Source fdset.
+ * @post `dst` is a copy of `src`.
+ */
+static inline void
+rb_fd_dup(rb_fdset_t *dst, const fd_set *src)
+{
+ *dst = *src;
+}
+
+RBIMPL_ATTR_PURE()
+/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */
+/**
+ * Raw pointer to `fd_set`.
+ *
+ * @param[in] f Target fdset.
+ * @return Underlying fd_set.
+ *
+ * @internal
+ *
+ * Extension library must not touch raw pointers. It was a bad idea to let
+ * them use it.
+ */
+static inline fd_set *
+rb_fd_ptr(rb_fdset_t *f)
+{
+ return f;
+}
+
+RBIMPL_ATTR_CONST()
+/**
+ * It seems this function has no use. Maybe just remove?
+ *
+ * @param[in] f A set.
+ * @return Number of file descriptors stored.
+ */
+static inline int
+rb_fd_max(const rb_fdset_t *f)
+{
+ return FD_SETSIZE;
+}
+
+/** @cond INTERNAL_MACRO */
+/* :FIXME: What are these? They don't exist for sibling implementations. */
+#define rb_fd_init_copy(d, s) (*(d) = *(s))
+#define rb_fd_term(f) ((void)(f))
+/** @endcond */
+
+#endif /* RBIMPL_INTERN_SELECT_POSIX_H */
diff --git a/include/ruby/internal/intern/select/win32.h b/include/ruby/internal/intern/select/win32.h
new file mode 100644
index 0000000000..b7301e63f3
--- /dev/null
+++ b/include/ruby/internal/intern/select/win32.h
@@ -0,0 +1,259 @@
+#ifndef RBIMPL_INTERN_SELECT_WIN32_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_SELECT_WIN32_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs to provide ::rb_fd_select().
+ */
+#include "ruby/internal/dosish.h" /* for rb_w32_select */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/assert.h"
+
+/**@cond INTERNAL_MACRO */
+#define rb_fd_zero rb_fd_zero
+#define rb_fd_clr rb_fd_clr
+#define rb_fd_isset rb_fd_isset
+#define rb_fd_copy rb_fd_copy
+#define rb_fd_dup rb_fd_dup
+#define rb_fd_ptr rb_fd_ptr
+#define rb_fd_max rb_fd_max
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+struct timeval;
+
+/**
+ * The data structure which wraps the fd_set bitmap used by select(2). This
+ * allows Ruby to use FD sets larger than that allowed by historic limitations
+ * on modern platforms.
+ */
+typedef struct {
+ int capa; /**< Maximum allowed number of FDs. */
+ fd_set *fdset; /**< File descriptors buffer. */
+} rb_fdset_t;
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * (Re-)initialises a fdset. One must be initialised before other `rb_fd_*`
+ * operations. Analogous to calling `malloc(3)` to allocate an `fd_set`.
+ *
+ * @param[out] f An fdset to squash.
+ * @post `f` holds no file descriptors.
+ *
+ * @internal
+ *
+ * Can't this leak memory if the same `f` is passed twice...?
+ */
+void rb_fd_init(rb_fdset_t *f);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Destroys the ::rb_fdset_t, releasing any memory and resources it used. It
+ * must be reinitialised using rb_fd_init() before future use. Analogous to
+ * calling `free(3)` to release memory for an `fd_set`.
+ *
+ * @param[out] f An fdset to squash.
+ * @post `f` holds no file descriptors.
+ */
+void rb_fd_term(rb_fdset_t *f);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Sets an fd to a fdset.
+ *
+ * @param[in] fd A file descriptor.
+ * @param[out] f Target fdset.
+ * @post `f` holds `fd`.
+ */
+void rb_fd_set(int fd, rb_fdset_t *f);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Destructively overwrites an fdset with another.
+ *
+ * @param[out] dst Target fdset.
+ * @param[in] src Source fdset.
+ * @param[in] max Maximum number of file descriptors to copy.
+ * @post `dst` is a copy of `src`.
+ */
+void rb_w32_fd_copy(rb_fdset_t *dst, const fd_set *src, int max);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_w32_fd_copy(), except it copies unlimited number of file
+ * descriptors.
+ *
+ * @param[out] dst Target fdset.
+ * @param[in] src Source fdset.
+ * @post `dst` is a copy of `src`.
+ */
+void rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_NOALIAS()
+/**
+ * Wipes out the current set of FDs.
+ *
+ * @param[out] f The fdset to clear.
+ * @post `f` has no FDs.
+ */
+static inline void
+rb_fd_zero(rb_fdset_t *f)
+{
+ f->fdset->fd_count = 0;
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Releases a specific FD from the given fdset.
+ *
+ * @param[in] n Target FD.
+ * @param[out] f The fdset that holds `n`.
+ * @post `f` doesn't hold n.
+ */
+static inline void
+rb_fd_clr(int n, rb_fdset_t *f)
+{
+ rb_w32_fdclr(n, f->fdset);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Queries if the given FD is in the given set.
+ *
+ * @param[in] n Target FD.
+ * @param[in] f The fdset to scan.
+ * @retval 1 Yes there is.
+ * @retval 0 No there isn't.
+ */
+static inline int
+rb_fd_isset(int n, rb_fdset_t *f)
+{
+ return rb_w32_fdisset(n, f->fdset);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Destructively overwrites an fdset with another.
+ *
+ * @param[out] dst Target fdset.
+ * @param[in] src Source fdset.
+ * @param[in] n Maximum number of file descriptors to copy.
+ * @post `dst` is a copy of `src`.
+ */
+static inline void
+rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int n)
+{
+ rb_w32_fd_copy(dst, src, n);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_fd_copy(), except it copies unlimited number of file
+ * descriptors.
+ *
+ * @param[out] dst Target fdset.
+ * @param[in] src Source fdset.
+ * @post `dst` is a copy of `src`.
+ */
+static inline void
+rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
+{
+ rb_w32_fd_dup(dst, src);
+}
+
+/**
+ * Waits for multiple file descriptors at once.
+ *
+ * @param[in] n Max FD in everything passed, plus one.
+ * @param[in,out] rfds Set of FDs to wait for reads.
+ * @param[in,out] wfds Set of FDs to wait for writes.
+ * @param[in,out] efds Set of FDs to wait for OOBs.
+ * @param[in,out] timeout Max blocking duration.
+ * @retval -1 Failed, errno set.
+ * @retval 0 Timeout exceeded.
+ * @retval otherwise Total number of file descriptors returned.
+ * @post `rfds` contains readable FDs.
+ * @post `wfds` contains writable FDs.
+ * @post `efds` contains exceptional FDs.
+ * @post `timeout` is the time left.
+ * @note All pointers are allowed to be null pointers.
+ *
+ * @internal
+ *
+ * This can wait for `SOCKET` and `HANDLE` at once. In order to achieve that
+ * property we heavily touch the internals of MSVCRT. We `CreateFile` a
+ * `"NUL"` alongside of a socket and directly manipulate its `struct ioinfo`.
+ * This is of course a very dirty hack. If we could design the API today we
+ * could use `CancelIoEx`. But we are older than that Win32 API.
+ */
+static inline int
+rb_fd_select(int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout)
+{
+ return rb_w32_select(
+ n,
+ rfds ? rfds->fdset : NULL,
+ wfds ? wfds->fdset : NULL,
+ efds ? efds->fdset : NULL,
+ timeout);
+}
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_PURE()
+/**
+ * Raw pointer to `fd_set`.
+ *
+ * @param[in] f Target fdset.
+ * @retval NULL `f` is already terminated by rb_fd_term().
+ * @retval otherwise Underlying fd_set.
+ *
+ * @internal
+ *
+ * Extension library must not touch raw pointers. It was a bad idea to let
+ * them use it.
+ */
+static inline fd_set *
+rb_fd_ptr(const rb_fdset_t *f)
+{
+ return f->fdset;
+}
+
+RBIMPL_ATTR_NONNULL(())
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/**
+ * It seems this function has no use. Maybe just remove?
+ *
+ * @param[in] f A set.
+ * @return Number of file descriptors stored.
+ */
+static inline int
+rb_fd_max(const rb_fdset_t *f)
+{
+ const fd_set *p = f->fdset;
+
+ RBIMPL_ASSERT_OR_ASSUME(p);
+ return RBIMPL_CAST((int)p->fd_count);
+}
+
+#endif /* RBIMPL_INTERN_SELECT_WIN32_H */
diff --git a/include/ruby/internal/intern/set.h b/include/ruby/internal/intern/set.h
new file mode 100644
index 0000000000..f4ff8665e2
--- /dev/null
+++ b/include/ruby/internal/intern/set.h
@@ -0,0 +1,111 @@
+#ifndef RBIMPL_INTERN_SET_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_SET_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cSet.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* set.c */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Iterates over a set. Calls func with each element of the set and the
+ * argument given. func should return ST_CONTINUE, ST_STOP, or ST_DELETE.
+ *
+ * @param[in] set An instance of ::rb_cSet to iterate over.
+ * @param[in] func Callback function to yield.
+ * @param[in] arg Passed as-is to `func`.
+ * @exception rb_eRuntimeError `set` was tampered during iterating.
+ */
+void rb_set_foreach(VALUE set, int (*func)(VALUE element, VALUE arg), VALUE arg);
+
+/**
+ * Creates a new, empty set object.
+ *
+ * @return An allocated new instance of ::rb_cSet.
+ */
+VALUE rb_set_new(void);
+
+/**
+ * Identical to rb_set_new(), except it additionally specifies how many elements
+ * it is expected to contain. This way you can create a set that is large enough
+ * for your need. For large sets, it means it won't need to be reallocated
+ * much, improving performance.
+ *
+ * @param[in] capa Designed capacity of the set.
+ * @return An empty Set, whose capacity is `capa`.
+ */
+VALUE rb_set_new_capa(size_t capa);
+
+/**
+ * Whether the set contains the given element.
+ *
+ * @param[in] set Set to look into.
+ * @param[in] element Set element to look for.
+ * @return true if element is in the set, falst otherwise.
+ */
+bool rb_set_lookup(VALUE set, VALUE element);
+
+/**
+ * Adds element to set.
+ *
+ * @param[in] set Target set table to modify.
+ * @param[in] element Arbitrary Ruby object.
+ * @exception rb_eFrozenError `set` is frozen.
+ * @return true if element was not already in set, false otherwise
+ * @post `element` is in `set`.
+ */
+bool rb_set_add(VALUE set, VALUE element);
+
+/**
+ * Removes all entries from set.
+ *
+ * @param[out] set Target to clear.
+ * @exception rb_eFrozenError `set`is frozen.
+ * @return The passed `set`
+ * @post `set` has no elements.
+ */
+VALUE rb_set_clear(VALUE set);
+
+/**
+ * Removes the element from from set.
+ *
+ * @param[in] set Target set to modify.
+ * @param[in] element Key to delete.
+ * @retval true if element was already in set, false otherwise
+ * @post `set` does not have `element` as an element.
+ */
+bool rb_set_delete(VALUE set, VALUE element);
+
+/**
+ * Returns the number of elements in the set.
+ *
+ * @param[in] set A set object.
+ * @return The size of the set.
+ */
+size_t rb_set_size(VALUE set);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_SET_H */
diff --git a/include/ruby/internal/intern/signal.h b/include/ruby/internal/intern/signal.h
new file mode 100644
index 0000000000..4773788651
--- /dev/null
+++ b/include/ruby/internal/intern/signal.h
@@ -0,0 +1,146 @@
+#ifndef RBIMPL_INTERN_SIGNAL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_SIGNAL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Signal handling APIs.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* signal.c */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Sends a signal ("kills") to processes.
+ *
+ * The first argument is the signal, either in:
+ *
+ * - Numerical representation (e.g. `9`), or
+ * - Textual representation of canonical (e.g. `:SIGKILL`) name or
+ * abbreviated (e.g. `:KILL`) name, either in ::rb_cSymbol or ::rb_cString.
+ *
+ * All the remaining arguments are numerical representations of process IDs.
+ * This function iterates over them to send the specified signal.
+ *
+ * You can specify both negative PIDs and negative signo to this function:
+ *
+ * ```
+ * sig \ pid | >= 1 | == 0 | == -1 | <= -2
+ * ===========+======+======+=======+=======
+ * > 0 | #1 | #2 | #3 | #4
+ * == 0 | #5 | #6 | #7 | #8
+ * < 0 | #9 | #10 | #11
+ * ```
+ *
+ * - Case #1: When signo and PID are both positive, this function sends the
+ * specified signal to the specified process (intuitive).
+ *
+ * - Case #2: When signo is positive and PID is zero, this function sends
+ * that signal to the current process group.
+ *
+ * - Case #3: When signo is positive and PID is -1, this function sends that
+ * signal to everything that the current process is allowed to kill.
+ *
+ * - Case #4: When signo is positive and PID is negative (but not -1), this
+ * function sends that signal to every processes in a process group, whose
+ * process group ID is the absolute value of the passed PID.
+ *
+ * - Case #5: When signo is zero and PID is positive, this function just
+ * checks for the existence of the specified process and doesn't send
+ * anything to anyone. In case the process is absent `Errno::ESRCH` is
+ * raised.
+ *
+ * - Case #6: When signo and PID are both zero, this function checks for the
+ * existence of the current process group. And it must do. This function
+ * is effectively a no-op then.
+ *
+ * - Case #7: When signo is zero and PID is -1, this function checks if there
+ * is any other process that the current process can kill. At least init
+ * (PID 1) must exist, so this must not fail.
+ *
+ * - Case #8: When signo is zero and PID is negative (but not -1), this
+ * function checks if there is a process group whose process group ID is
+ * the absolute value of the passed PID. In case the process group is
+ * absent `Errno::ESRCH` is raised.
+ *
+ * - Case #9: When signo is negative and PID is positive, this function sends
+ * the absolute value of the passed signo to the process group specified as
+ * the PID.
+ *
+ * - Case #10: When signo is negative and PID is zero, it is highly expected
+ * that this function sends the absolute value of the passed signo to the
+ * current process group. Strictly speaking, IEEE Std 1003.1-2017
+ * specifies that this (`killpg(3posix)` with an argument of zero) is an
+ * undefined behaviour. But no operating system is known so far that does
+ * things differently.
+ *
+ * - Case #11: When signo and PID are both negative, the behaviour of this
+ * function depends on how `killpg(3)` works. On Linux, it seems such
+ * attempt is strictly prohibited and `Errno::EINVAL` is raised. But on
+ * macOS, it seems it tries to send the signal actually to the process
+ * group.
+ *
+ * @note Above description is in fact different from how `kill(2)` works.
+ * We interpret the passed arguments before passing them through to
+ * system calls.
+ * @param[in] argc Number of objects in `argv`.
+ * @param[in] argv Signal, followed by target PIDs.
+ * @exception rb_eArgError Unknown signal name.
+ * @exception rb_eSystemCallError Various errors sending signal to processes.
+ * @return Something numeric. The meaning of this return value is unclear.
+ * It seems in case of #1 above, this could be the body count. But
+ * other cases remain mysterious.
+ */
+VALUE rb_f_kill(int argc, const VALUE *argv);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries the name of the signal. It returns for instance `"KILL"` for
+ * SIGKILL.
+ *
+ * @param[in] signo Signal number to query.
+ * @retval 0 No such signal.
+ * @retval otherwise A pointer to a static C string that is the name of
+ * the signal.
+ * @warning Don't free the return value.
+ */
+const char *ruby_signal_name(int signo);
+
+/**
+ * Pretends as if there was no custom signal handler. This function sets the
+ * signal action to SIG_DFL, then kills itself.
+ *
+ * @param[in] sig The signal.
+ * @post Previous signal handler is lost.
+ * @post Passed signal is sent to the current process.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't understand the needs of this function being visible from
+ * extension libraries.
+ */
+void ruby_default_signal(int sig);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_SIGNAL_H */
diff --git a/include/ruby/internal/intern/sprintf.h b/include/ruby/internal/intern/sprintf.h
new file mode 100644
index 0000000000..aedc0f9ab1
--- /dev/null
+++ b/include/ruby/internal/intern/sprintf.h
@@ -0,0 +1,159 @@
+#ifndef RBIMPL_INTERN_SPRINTF_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_SPRINTF_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Our own private `printf(3)`.
+ */
+#include "ruby/internal/attr/format.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* sprintf.c */
+
+/**
+ * Identical to rb_str_format(), except how the arguments are arranged.
+ *
+ * @param[in] argc Number of objects of `argv`.
+ * @param[in] argv A format string, followed by its arguments.
+ * @return A rendered new instance of ::rb_cString.
+ *
+ * @internal
+ *
+ * You can safely pass NULL to `argv`. Doesn't make any sense though.
+ */
+VALUE rb_f_sprintf(int argc, const VALUE *argv);
+
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+/**
+ * Ruby's extended `sprintf(3)`. We ended up reinventing the entire `printf`
+ * business because we don't want to depend on locales. OS-provided `printf`
+ * routines might or might not, which caused instabilities of the result
+ * strings.
+ *
+ * The format sequence is a mixture of format specifiers and other verbatim
+ * contents. Each format specifier starts with a `%`, and has the following
+ * structure:
+ *
+ * ```
+ * %[flags][width][.precision][length]conversion
+ * ```
+ *
+ * This function supports flags of ` `, `#`, `+`, `-`, `0`, width of
+ * non-negative decimal integer and `*`, precision of non-negative decimal
+ * integers and `*`, length of `L`, `h`, `t`, `z`, `l`, `ll`, `q`, conversions
+ * of `A`, `D`, `E`, `G`, `O`, `U`, `X`, `a`, `c`, `d`, `e`, `f`, `g`, `i`,
+ * `n`, `o`, `p`, `s`, `u`, `x`, and `%`. In case of `_WIN32` it also supports
+ * `I`. And additionally, it supports magical `PRIsVALUE` macro that can
+ * stringise arbitrary Ruby objects:
+ *
+ * ```CXX
+ * rb_sprintf("|%"PRIsVALUE"|", RUBY_Qtrue); // => "|true|"
+ * rb_sprintf("%+"PRIsVALUE, rb_stdin); // => "#<IO:<STDIN>>"
+ * ```
+ *
+ * @param[in] fmt A `printf`-like format specifier.
+ * @param[in] ... Variadic number of contents to format.
+ * @return A rendered new instance of ::rb_cString.
+ *
+ * @internal
+ *
+ * :FIXME: We can improve this document.
+ */
+VALUE rb_sprintf(const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 0)
+/**
+ * Identical to rb_sprintf(), except it takes a `va_list`.
+ *
+ * @param[in] fmt A `printf`-like format specifier.
+ * @param[in] ap Contents to format.
+ * @return A rendered new instance of ::rb_cString.
+ */
+VALUE rb_vsprintf(const char *fmt, va_list ap);
+
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 3)
+/**
+ * Identical to rb_sprintf(), except it renders the output to the specified
+ * object rather than creating a new one.
+ *
+ * @param[out] dst String to modify.
+ * @param[in] fmt A `printf`-like format specifier.
+ * @param[in] ... Variadic number of contents to format.
+ * @exception rb_eTypeError `dst` is not a String.
+ * @return Passed `dst`.
+ * @post `dst` has the rendered output appended to its end.
+ */
+VALUE rb_str_catf(VALUE dst, const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((2))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 0)
+/**
+ * Identical to rb_str_catf(), except it takes a `va_list`. It can also be
+ * seen as a routine identical to rb_vsprintf(), except it renders the output
+ * to the specified object rather than creating a new one.
+ *
+ * @param[out] dst String to modify.
+ * @param[in] fmt A `printf`-like format specifier.
+ * @param[in] ap Contents to format.
+ * @exception rb_eTypeError `dst` is not a String.
+ * @return Passed `dst`.
+ * @post `dst` has the rendered output appended to its end.
+ */
+VALUE rb_str_vcatf(VALUE dst, const char *fmt, va_list ap);
+
+/**
+ * Formats a string.
+ *
+ * Returns the string resulting from applying `fmt` to `argv`. The format
+ * sequence is a mixture of format specifiers and other verbatim contents.
+ * Each format specifier starts with a `%`, and has the following structure:
+ *
+ * ```
+ * %[flags][width][.precision]type
+ * ```
+ *
+ * ... which is different from that of rb_sprintf(). Because ruby has no
+ * `short` or `long`, there is no way to specify a "length" of an argument.
+ *
+ * This function supports flags of ` `, `#`, `+`, `-`, `<>`, `{}`, with of
+ * non-negative decimal integer and `$`, `*`, precision of non-negative decimal
+ * integer and `$`, `*`, type of `A`, `B`, `E`, `G`, `X`, `a`, `b`, `c`, `d`,
+ * `e`, `f`, `g`, `i`, `o`, `p`, `s`, `u`, `x`, `%`. This list is also
+ * (largely the same but) not identical to that of rb_sprintf().
+ *
+ * @param[in] argc Number of objects in `argv`.
+ * @param[in] argv Format arguments.
+ * @param[in] fmt A printf-like format specifier.
+ * @exception rb_eTypeError `fmt` is not a string.
+ * @exception rb_eArgError Failed to parse `fmt`.
+ * @return A rendered new instance of ::rb_cString.
+ * @note Everything it takes must be Ruby objects.
+ *
+ */
+VALUE rb_str_format(int argc, const VALUE *argv, VALUE fmt);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_SPRINTF_H */
diff --git a/include/ruby/internal/intern/string.h b/include/ruby/internal/intern/string.h
new file mode 100644
index 0000000000..8bd1ffcfb4
--- /dev/null
+++ b/include/ruby/internal/intern/string.h
@@ -0,0 +1,1758 @@
+#ifndef RBIMPL_INTERN_STRING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_STRING_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cString.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/constant_p.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/variable.h" /* rb_gvar_setter_t */
+#include "ruby/st.h" /* st_index_t */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* string.c */
+
+/**
+ * Allocates an instance of ::rb_cString.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString, of `len` bytes length, of
+ * "binary" encoding, whose contents are verbatim copy of `ptr`.
+ * @pre At least `len` bytes of continuous memory region shall be
+ * accessible via `ptr`.
+ */
+VALUE rb_str_new(const char *ptr, long len);
+
+/**
+ * Identical to rb_str_new(), except it assumes the passed pointer is a pointer
+ * to a C string.
+ *
+ * @param[in] ptr A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @exception rb_eArgError `ptr` is a null pointer.
+ * @return An instance of ::rb_cString, of "binary" encoding, whose
+ * contents are verbatim copy of `ptr`.
+ * @pre `ptr` must not be a null pointer.
+ */
+VALUE rb_str_new_cstr(const char *ptr);
+
+/**
+ * Identical to rb_str_new_cstr(), except it takes a Ruby's string instead of
+ * C's. Implementation wise it creates a string that shares the backend memory
+ * region with the receiver. So the name. But there is no way for extension
+ * libraries to know if a string is of such variant.
+ *
+ * @param[in] str An object of ::RString.
+ * @return An allocated instance of ::rb_cString, which shares the
+ * encoding, length, and contents with the passed string.
+ * @pre `str` must not be any arbitrary object except ::RString.
+ * @note Use #StringValue to enforce the precondition.
+ */
+VALUE rb_str_new_shared(VALUE str);
+
+/**
+ * Creates a frozen copy of the string, if necessary. This function does
+ * nothing when the passed string is already frozen. Otherwise, it allocates a
+ * copy of it, which is frozen. The passed string is untouched either ways.
+ *
+ * @param[in] str An object of ::RString.
+ * @return Something frozen.
+ * @pre `str` must not be any arbitrary object except ::RString.
+ * @note Use #StringValue to enforce the precondition.
+ */
+VALUE rb_str_new_frozen(VALUE str);
+
+/**
+ * Identical to rb_str_new(), except it takes the class of the allocating
+ * object.
+ *
+ * @param[in] obj A string-ish object.
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of the class of `obj`, of `len` bytes length, of
+ * "binary" encoding, whose contents are verbatim copy of `ptr`.
+ * @pre At least `len` bytes of continuous memory region shall be
+ * accessible via `ptr`.
+ *
+ * @internal
+ *
+ * Why it doesn't take an instance of ::rb_cClass?
+ */
+VALUE rb_str_new_with_class(VALUE obj, const char *ptr, long len);
+
+/**
+ * Identical to rb_str_new(), except it generates a string of "default
+ * external" encoding.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to "default external" is fully defined over
+ * the given contents, then the return value is a string of
+ * "default external" encoding, whose contents are the converted
+ * ones. Otherwise the string is a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ */
+VALUE rb_external_str_new(const char *ptr, long len);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_external_str_new(), except it assumes the passed pointer is
+ * a pointer to a C string. It can also be seen as a routine identical to
+ * rb_str_new_cstr(), except it generates a string of "default external"
+ * encoding.
+ *
+ * @param[in] ptr A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to "default external" is fully defined over
+ * the given contents, then the return value is a string of
+ * "default external" encoding, whose contents are the converted
+ * ones. Otherwise the string is a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ * @pre `ptr` must not be a null pointer.
+ */
+VALUE rb_external_str_new_cstr(const char *ptr);
+
+/**
+ * Identical to rb_str_new(), except it generates a string of "locale"
+ * encoding. It can also be seen as a routine identical to
+ * rb_external_str_new(), except it generates a string of "locale" encoding
+ * instead of "default external" encoding.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to "locale" is fully defined over the given
+ * contents, then the return value is a string of "locale"
+ * encoding, whose contents are the converted ones. Otherwise the
+ * string is a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ */
+VALUE rb_locale_str_new(const char *ptr, long len);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_locale_str_new(), except it assumes the passed pointer is a
+ * pointer to a C string. It can also be seen as a routine identical to
+ * rb_external_str_new_cstr(), except it generates a string of "locale"
+ * encoding instead of "default external".
+ *
+ * @param[in] ptr A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to "locale" is fully defined over the given
+ * contents, then the return value is a string of "locale"
+ * encoding, whose contents are the converted ones. Otherwise the
+ * string is a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ * @pre `ptr` must not be a null pointer.
+ */
+VALUE rb_locale_str_new_cstr(const char *ptr);
+
+/**
+ * Identical to rb_str_new(), except it generates a string of "filesystem"
+ * encoding. It can also be seen as a routine identical to
+ * rb_external_str_new(), except it generates a string of "filesystem" encoding
+ * instead of "default external" encoding.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to "filesystem" is fully defined over the
+ * given contents, then the return value is a string of
+ * "filesystem" encoding, whose contents are the converted ones.
+ * Otherwise the string is a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ */
+VALUE rb_filesystem_str_new(const char *ptr, long len);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_filesystem_str_new(), except it assumes the passed pointer
+ * is a pointer to a C string. It can also be seen as a routine identical to
+ * rb_external_str_new_cstr(), except it generates a string of "filesystem"
+ * encoding instead of "default external".
+ *
+ * @param[in] ptr A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to "filesystem" is fully defined over the
+ * given contents, then the return value is a string of
+ * "filesystem" encoding, whose contents are the converted ones.
+ * Otherwise the string is a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ * @pre `ptr` must not be a null pointer.
+ */
+VALUE rb_filesystem_str_new_cstr(const char *ptr);
+
+/**
+ * Allocates a "string buffer". A string buffer here is an instance of
+ * ::rb_cString, whose capacity is bigger than the length of it. If you can
+ * say that a string grows to a specific amount of bytes, this could be
+ * effective than resizing a string over and over again and again.
+ *
+ * @param[in] capa Designed capacity of the generating string.
+ * @return An empty string, of "binary" encoding, whose capacity is `capa`.
+ */
+VALUE rb_str_buf_new(long capa);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is a rb_str_buf_new() + rb_str_buf_cat() combo.
+ *
+ * @param[in] ptr A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString, of "binary" encoding, whose
+ * contents are verbatim copy of `ptr`.
+ * @pre `ptr` must not be a null pointer.
+ *
+ * @internal
+ *
+ * This must be identical to rb_str_new_cstr(), except done in inefficient way?
+ * @shyouhei doesn't understand why this is not a simple alias.
+ */
+VALUE rb_str_buf_new_cstr(const char *ptr);
+
+/**
+ * Allocates a "temporary" string. This is a hidden empty string. Handy on
+ * occasions.
+ *
+ * @param[in] len Designed length of the string.
+ * @return A hidden, empty string.
+ * @see rb_obj_hide()
+ */
+VALUE rb_str_tmp_new(long len);
+
+/**
+ * Identical to rb_str_new(), except it generates a string of "US ASCII"
+ * encoding. This is different from rb_external_str_new(), not only for the
+ * output encoding, but also it doesn't convert the contents.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString, of `len` bytes length, of
+ * "US ASCII" encoding, whose contents are verbatim copy of `ptr`.
+ */
+VALUE rb_usascii_str_new(const char *ptr, long len);
+
+/**
+ * Identical to rb_str_new_cstr(), except it generates a string of "US ASCII"
+ * encoding. It can also be seen as a routine Identical to
+ * rb_usascii_str_new(), except it assumes the passed pointer is a pointer to a
+ * C string.
+ *
+ * @param[in] ptr A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @exception rb_eArgError `ptr` is a null pointer.
+ * @return An instance of ::rb_cString, of "US ASCII" encoding, whose
+ * contents are verbatim copy of `ptr`.
+ * @pre `ptr` must not be a null pointer.
+ */
+VALUE rb_usascii_str_new_cstr(const char *ptr);
+
+/**
+ * Identical to rb_str_new(), except it generates a string of "UTF-8" encoding.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString, of `len` bytes length, of
+ * "UTF-8" encoding, whose contents are verbatim copy of `ptr`.
+ */
+VALUE rb_utf8_str_new(const char *ptr, long len);
+
+/**
+ * Identical to rb_str_new_cstr(), except it generates a string of "UTF-8"
+ * encoding. It can also be seen as a routine Identical to
+ * rb_usascii_str_new(), except it assumes the passed pointer is a pointer to a
+ * C string.
+ *
+ * @param[in] ptr A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @exception rb_eArgError `ptr` is a null pointer.
+ * @return An instance of ::rb_cString, of "UTF-8" encoding, whose contents
+ * are verbatim copy of `ptr`.
+ * @pre `ptr` must not be a null pointer.
+ */
+VALUE rb_utf8_str_new_cstr(const char *ptr);
+
+/**
+ * @name Special strings that are backended by C string literals.
+ *
+ * *_str_new_static functions are intended for C string literals.
+ * They require memory in the range [ptr, ptr+len] to always be readable.
+ * Note that this range covers a total of len + 1 bytes.
+ *
+ * @{
+ */
+
+/**
+ * Identical to rb_str_new(), except it takes a C string literal.
+ *
+ * @param[in] ptr A C string literal.
+ * @param[in] len `strlen(ptr)`.
+ * @exception rb_eArgError `len` out of range of `size_t`.
+ * @pre `ptr` must be a C string constant.
+ * @return An instance of ::rb_cString, of "binary" encoding, whose backend
+ * storage is the passed C string literal.
+ * @warning It is a very bad idea to write to a C string literal (often
+ * immediate SEGV shall occur). Consider return values of this
+ * function be read-only.
+ *
+ * @internal
+ *
+ * Surprisingly it can take NULL, and generates an empty string.
+ */
+VALUE rb_str_new_static(const char *ptr, long len);
+
+/**
+ * Identical to rb_str_new_static(), except it generates a string of "US ASCII"
+ * encoding instead of "binary". It can also be seen as a routine identical to
+ * rb_usascii_str_new(), except it takes a C string literal.
+ *
+ * @param[in] ptr A C string literal.
+ * @param[in] len `strlen(ptr)`.
+ * @exception rb_eArgError `len` out of range of `size_t`.
+ * @pre `ptr` must be a C string constant.
+ * @return An instance of ::rb_cString, of "US ASCII" encoding, whose
+ * backend storage is the passed C string literal.
+ * @warning It is a very bad idea to write to a C string literal (often
+ * immediate SEGV shall occur). Consider return values of this
+ * function be read-only.
+ */
+VALUE rb_usascii_str_new_static(const char *ptr, long len);
+
+/**
+ * Identical to rb_str_new_static(), except it generates a string of "UTF-8"
+ * encoding instead of "binary". It can also be seen as a routine identical to
+ * rb_utf8_str_new(), except it takes a C string literal.
+ *
+ * @param[in] ptr A C string literal.
+ * @param[in] len `strlen(ptr)`.
+ * @exception rb_eArgError `len` out of range of `size_t`.
+ * @pre `ptr` must be a C string constant.
+ * @return An instance of ::rb_cString, of "UTF-8" encoding, whose backend
+ * storage is the passed C string literal.
+ * @warning It is a very bad idea to write to a C string literal (often
+ * immediate SEGV shall occur). Consider return values of this
+ * function be read-only.
+ */
+VALUE rb_utf8_str_new_static(const char *ptr, long len);
+
+/** @} */
+
+/**
+ * Identical to rb_interned_str(), except it takes a Ruby's string instead of
+ * C's and preserves its encoding. It can also be seen as a routine identical
+ * to rb_str_new_shared(), except it returns an infamous "f"string.
+ *
+ * @param[in] str An object of ::RString.
+ * @return An instance of ::rb_cString, either cached or allocated, which
+ * has the identical encoding, length, and contents with the passed
+ * string.
+ * @pre `str` must not be any arbitrary object except ::RString.
+ * @note Use #StringValue to enforce the precondition.
+ *
+ * @internal
+ *
+ * It actually finds or creates a fstring of the needed property, and
+ * destructively modifies the receiver behind-the-scene so that it becomes a
+ * shared string whose parent is the returning fstring.
+ */
+VALUE rb_str_to_interned_str(VALUE str);
+
+/**
+ * Identical to rb_str_new(), except it returns an infamous "f"string. What is
+ * a fstring? Well it is a special subkind of strings that is immutable,
+ * deduped globally, and managed by our GC. It is much like a Symbol (in fact
+ * Symbols are dynamic these days and are backended using fstrings). This
+ * concept has been silently introduced at some point in 2.x era. Since then
+ * it gained wider acceptance in the core. Starting from 3.x extension
+ * libraries can also generate ones.
+ *
+ * @param[in] ptr A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eArgError `len` is negative.
+ * @return A found or created instance of ::rb_cString, of `len` bytes
+ * length, whose contents are identical to that of `ptr`. Its
+ * encoding will be US-ASCII if all bytes are lower ASCII, BINARY
+ * otherwise.
+ * @pre At least `len` bytes of continuous memory region shall be
+ * accessible via `ptr`.
+ */
+VALUE rb_interned_str(const char *ptr, long len);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_interned_str(), except it assumes the passed pointer is a
+ * pointer to a C's string. It can also be seen as a routine identical to
+ * rb_str_to_interned_str(), except it takes a C's string instead of Ruby's.
+ * Or it can also be seen as a routine identical to rb_str_new_cstr(), except
+ * it returns an infamous "f"string.
+ *
+ * @param[in] ptr A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString, whose contents are verbatim copy
+ * of `ptr`. Its encoding will be US-ASCII if all bytes are lower
+ * ASCII, BINARY otherwise.
+ * @pre `ptr` must not be a null pointer.
+ */
+VALUE rb_interned_str_cstr(const char *ptr);
+
+/**
+ * Destroys the given string for no reason.
+ *
+ * @warning DO NOT USE IT.
+ * @warning Leave this task to our GC.
+ * @warning It was a bad idea at the first place to let you know about it.
+ *
+ * @param[out] str The string to be executed.
+ * @post The given string no longer exists.
+ * @note Maybe `String#clear` could be what you want.
+ *
+ * @internal
+ *
+ * Should have moved this to `internal/string.h`.
+ */
+void rb_str_free(VALUE str);
+
+/**
+ * Replaces the contents of the former with the latter.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Source object.
+ * @pre Both objects must not be any arbitrary objects except
+ * ::RString.
+ * @post `dst`'s former components are abandoned. It now has the
+ * identical encoding, length, and contents to `src`.
+ * @see rb_str_replace()
+ *
+ * @internal
+ *
+ * @shyouhei doesn't understand why this is useful to extension libraries.
+ * Just use rb_str_replace(). What's wrong with that?
+ */
+void rb_str_shared_replace(VALUE dst, VALUE src);
+
+/**
+ * Identical to rb_str_cat_cstr(), except it takes Ruby's string instead of
+ * C's. It can also be seen as a routine identical to rb_str_shared_replace(),
+ * except it appends instead of replaces.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Source object.
+ * @exception rb_eEncCompatError Can't mix the encodings.
+ * @exception rb_eArgError Result string too big.
+ * @return The passed `dst`.
+ * @pre Both objects must not be any arbitrary objects except
+ * ::RString.
+ * @post `dst` has the contents of `src` appended, with encoding
+ * converted into `dst`'s one, into the end of `dst`.
+ */
+VALUE rb_str_buf_append(VALUE dst, VALUE src);
+
+/** @alias{rb_str_cat} */
+VALUE rb_str_buf_cat(VALUE, const char*, long);
+
+/** @alias{rb_str_cat_cstr} */
+VALUE rb_str_buf_cat2(VALUE, const char*);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_str_cat_cstr(), except it additionally assumes the source
+ * string be a NUL terminated ASCII string.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Source string.
+ * @exception rb_eArgError Result string too big.
+ * @return The passed `dst`.
+ * @pre `dst` must not be any arbitrary object except ::RString.
+ * @pre `src` must be a NUL terminated ASCII string.
+ * @post `dst` has the contents of `src` appended, with encoding
+ * converted into `dst`'s one, into the end of `dst`.
+ */
+VALUE rb_str_buf_cat_ascii(VALUE dst, const char *src);
+
+/**
+ * Try converting an object to its stringised representation using its `to_s`
+ * method, if any. If there is no such thing, it resorts to rb_any_to_s()
+ * output.
+ *
+ * @param[in] obj Arbitrary ruby object to stringise.
+ * @return An instance of ::rb_cString.
+ */
+VALUE rb_obj_as_string(VALUE obj);
+
+/**
+ * Try converting an object to its stringised representation using its `to_str`
+ * method, if any. If there is no such thing, returns ::RUBY_Qnil.
+ *
+ * @param[in] obj Arbitrary ruby object to stringise.
+ * @exception rb_eTypeError `obj.to_str` returned something non-String.
+ * @retval RUBY_Qnil No conversion from obj to String defined.
+ * @return otherwise Stringised representation of `obj`.
+ * @see rb_io_check_io
+ * @see rb_check_array_type
+ * @see rb_check_hash_type
+ */
+VALUE rb_check_string_type(VALUE obj);
+
+/**
+ * Asserts that the given string's encoding is (Ruby's definition of) ASCII
+ * compatible.
+ *
+ * @param[in] obj An instance of ::rb_cString.
+ * @exception rb_eEncCompatError `obj` is ASCII incompatible.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't know if this is an Easter egg or an official feature, but
+ * this function can in fact take non-strings such as Symbols, Regexps, IOs,
+ * etc. However if something unsupported is passed, it causes SEGV. It seems
+ * the feature is kind of untested.
+ */
+void rb_must_asciicompat(VALUE obj);
+
+/**
+ * Duplicates a string.
+ *
+ * @param[in] str String in question to duplicate.
+ * @return A duplicated new instance.
+ * @pre `str` must be of ::RString.
+ */
+VALUE rb_str_dup(VALUE str);
+
+/**
+ * Like rb_str_dup(), but always create an instance of ::rb_cString
+ * regardless of the given object's class. This makes the most sense
+ * when the passed string is formerly hidden by rb_obj_hide().
+ *
+ * @param[in] str A string, possibly hidden.
+ * @return A duplicated new instance of ::rb_cString.
+ */
+VALUE rb_str_resurrect(VALUE str);
+
+/**
+ * Obtains a "temporary lock" of the string. This advisory locking mechanism
+ * prevents other cooperating threads from tampering the receiver. The same
+ * thing could be done via freeze mechanism, but this one can also be unlocked
+ * using rb_str_unlocktmp().
+ *
+ * @param[out] str String to lock.
+ * @exception rb_eRuntimeError `str` already locked.
+ * @return The given string.
+ * @post The string is locked.
+ */
+VALUE rb_str_locktmp(VALUE str);
+
+/**
+ * Releases a lock formerly obtained by rb_str_locktmp().
+ *
+ * @param[out] str String to unlock.
+ * @exception rb_eRuntimeError `str` already unlocked.
+ * @return The given string.
+ * @post The string is locked.
+ */
+VALUE rb_str_unlocktmp(VALUE str);
+
+/** @alias{rb_str_new_frozen} */
+VALUE rb_str_dup_frozen(VALUE);
+
+/** @alias{rb_str_new_frozen} */
+#define rb_str_dup_frozen rb_str_new_frozen
+
+/**
+ * Generates a new string, concatenating the former to the latter. It can also
+ * be seen as a routine identical to rb_str_append(), except it doesn't tamper
+ * the passed strings to create a new one instead.
+ *
+ * @param[in] lhs Source string #1.
+ * @param[in] rhs Source string #2.
+ * @exception rb_eEncCompatError Can't mix the encodings.
+ * @exception rb_eArgError Result string too big.
+ * @return A new string containing `rhs` concatenated to `lhs`.
+ * @pre Both objects must not be any arbitrary objects except ::RString.
+ * @note This operation doesn't commute. Don't get confused by the
+ * "plus" terminology. For historical reasons there are some
+ * noncommutative `+`s in Ruby. This is one of such things. There
+ * has been a long discussion around `+`s in programming languages.
+ */
+VALUE rb_str_plus(VALUE lhs, VALUE rhs);
+
+/**
+ * Repetition of a string.
+ *
+ * @param[in] str String to repeat.
+ * @param[in] num Count, something numeric.
+ * @exception rb_eArgError `num` is negative.
+ * @return A new string repeating `num` times of `str`.
+ */
+VALUE rb_str_times(VALUE str, VALUE num);
+
+/**
+ * Byte offset to character offset conversion. This makes sense when the
+ * receiver is in a multibyte encoding. The string's i-th character does not
+ * always sit at its i-th byte. This function scans the contents to find the
+ * character index that matches the byte index. Generally speaking this is an
+ * `O(n)` operation. Could be slow.
+ *
+ * @param[in] str The string to scan.
+ * @param[in] pos Offset, in bytes.
+ * @return Offset, in characters.
+ */
+long rb_str_sublen(VALUE str, long pos);
+
+/**
+ * This is the implementation of two-argumented `String#slice`.
+ *
+ * - Returns the substring of the given `len` found in `str` at offset `beg`:
+ *
+ * ```ruby
+ * 'foo'[0, 2] # => "fo"
+ * 'foo'[0, 0] # => ""
+ * ```
+ *
+ * - Counts backward from the end of `str` if `beg` is negative:
+ *
+ * ```ruby
+ * 'foo'[-2, 2] # => "oo"
+ * ```
+ *
+ * - Special case: returns a new empty string if `beg` is equal to the length
+ * of `str`:
+ *
+ * ```ruby
+ * 'foo'[3, 2] # => ""
+ * ```
+ *
+ * - Returns a null pointer if `beg` is out of range:
+ *
+ * ```ruby
+ * 'foo'[4, 2] # => nil
+ * 'foo'[-4, 2] # => nil
+ * ```
+ *
+ * - Returns the trailing substring of `str` if `len` is large:
+ *
+ * ```ruby
+ * 'foo'[1, 50] # => "oo"
+ * ```
+ *
+ * - Returns a null pointer if `len` is negative:
+ *
+ * ```ruby
+ * 'foo'[0, -1] # => nil
+ * ```
+ *
+ * @param[in] str The string to slice.
+ * @param[in] beg Requested offset of the substring.
+ * @param[in] len Requested length of the substring.
+ * @retval RUBY_Qnil Parameters out of range.
+ * @retval otherwise A new string whose contents is the specified
+ * substring of `str`.
+ * @pre `str` must not be any arbitrary objects except ::RString.
+ */
+VALUE rb_str_substr(VALUE str, long beg, long len);
+
+/**
+ * Identical to rb_str_substr(), except the numbers are interpreted as byte
+ * offsets instead of character offsets.
+ *
+ * @param[in] str The string to slice.
+ * @param[in] beg Requested offset of the substring.
+ * @param[in] len Requested length of the substring.
+ * @return A new string whose contents is the specified substring of `str`.
+ * @pre `str` must not be any arbitrary objects except ::RString.
+ * @pre `beg` and `len` must not point to OOB contents.
+ */
+VALUE rb_str_subseq(VALUE str, long beg, long len);
+
+/**
+ * Identical to rb_str_substr(), except it returns a C's string instead of
+ * Ruby's.
+ *
+ * @param[in] str The string to slice.
+ * @param[in] beg Requested offset of the substring.
+ * @param[in,out] len Requested length of the substring.
+ * @retval NULL Parameters out of range.
+ * @retval otherwise A pointer inside of `str`'s backend storage where
+ * the specified substring exist.
+ * @pre `str` must not be any arbitrary objects except ::RString.
+ * @post `len` is updated to have the length of the return value.
+ */
+char *rb_str_subpos(VALUE str, long beg, long *len);
+
+/**
+ * Declares that the string is about to be modified. This for instance let the
+ * string have a dedicated backend storage.
+ *
+ * @param[out] str String about to be modified.
+ * @exception rb_eRuntimeError `str` is `locktmp`-ed.
+ * @exception rb_eFrozenError `str` is frozen.
+ * @pre `str` must not be any arbitrary objects except ::RString.
+ * @post Upon successful return the passed string is eligible to be
+ * modified.
+ */
+void rb_str_modify(VALUE str);
+
+/**
+ * Identical to rb_str_modify(), except it additionally expands the capacity of
+ * the receiver.
+ *
+ * @param[out] str Target string to modify.
+ * @param[in] capa Additional capacity to add.
+ * @exception rb_eArgError `capa` is negative.
+ * @exception rb_eRuntimeError `str` is `locktmp`-ed.
+ * @exception rb_eFrozenError `str` is frozen.
+ * @pre `str` must not be any arbitrary objects except ::RString.
+ * @post Upon successful return the passed string is modified so that
+ * its capacity is increased for `capa` bytes.
+ */
+void rb_str_modify_expand(VALUE str, long capa);
+
+/**
+ * This is the implementation of `String#freeze`.
+ *
+ * @param[out] str Target string to freeze.
+ * @return The passed string.
+ * @post Upon successful return the passed string is frozen.
+ */
+VALUE rb_str_freeze(VALUE str);
+
+/**
+ * Overwrites the length of the string. Typically this is used to shrink a
+ * string that was formerly expanded.
+ *
+ * ```CXX
+ * extern int fd;
+ * auto str = rb_eval_string("'...'");
+ * rb_str_modify_expand(str, BUFSIZ);
+ * if (auto len = recv(fd, RSTRING_PTR(str), BUFSIZ, 0); len >= 0) {
+ * rb_str_set_len(str, len);
+ * }
+ * else {
+ * rb_sys_fail("recv(2)");
+ * }
+ * ```
+ *
+ * @param[out] str String to shrink.
+ * @param[in] len New length of the string.
+ * @exception rb_eRuntimeError `str` is `locktmp`-ed.
+ * @exception rb_eFrozenError `str` is frozen.
+ * @pre `str` must not be any arbitrary objects except ::RString.
+ * @post Upon successful return `str`'s length is set to `len`.
+ */
+void rb_str_set_len(VALUE str, long len);
+
+/**
+ * Overwrites the length of the string. In contrast to rb_str_set_len(), this
+ * function can also expand a string.
+ *
+ * @param[out] str String to shrink.
+ * @param[in] len New length of the string.
+ * @exception rb_eArgError `len` is negative.
+ * @exception rb_eRuntimeError `str` is `locktmp`-ed.
+ * @exception rb_eFrozenError `str` is frozen.
+ * @return The passed `str`.
+ * @pre `str` must not be any arbitrary objects except ::RString.
+ * @post Upon successful return `str` is either expanded or shrunken to
+ * have its length be `len`.
+ */
+VALUE rb_str_resize(VALUE str, long len);
+
+/**
+ * Destructively appends the passed contents to the string.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Contents to append.
+ * @param[in] srclen Length of `src`.
+ * @exception rb_eArgError `srclen` is negative.
+ * @return The passed `dst`.
+ * @pre `dst` must not be any arbitrary objects except ::RString.
+ * @post `dst` has the contents of `ptr` appended.
+ */
+VALUE rb_str_cat(VALUE dst, const char *src, long srclen);
+
+/**
+ * Identical to rb_str_cat(), except it assumes the passed pointer is a pointer
+ * to a C string.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Contents to append.
+ * @exception rb_eArgError Result string too big.
+ * @exception rb_eArgError `src` is a null pointer.
+ * @return The passed `dst`.
+ * @pre `dst` must not be any arbitrary objects except ::RString.
+ * @pre `src` must not be a null pointer.
+ * @post `dst` has the contents of `src` appended.
+ */
+VALUE rb_str_cat_cstr(VALUE dst, const char *src);
+
+/** @alias{rb_str_cat_cstr} */
+VALUE rb_str_cat2(VALUE, const char*);
+
+/**
+ * Identical to rb_str_buf_append(), except it converts the right hand side
+ * before concatenating.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Source object.
+ * @exception rb_eEncCompatError Can't mix the encodings.
+ * @exception rb_eArgError Result string too big.
+ * @return The passed `dst`.
+ * @pre `dst` must not be any arbitrary objects except ::RString.
+ * @post `dst` has the contents of `src` appended, with encoding
+ * converted into `dst`'s one, into the end of `dst`.
+ */
+VALUE rb_str_append(VALUE dst, VALUE src);
+
+/**
+ * Identical to rb_str_append(), except it also accepts an integer as a
+ * codepoint. This resembles `String#<<`.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Source object, String or Numeric.
+ * @exception rb_eRangeError Source numeric is out of range.
+ * @exception rb_eEncCompatError Source string too long.
+ * @exception rb_eArgError Result string too big.
+ * @return The passed `dst`.
+ * @pre `dst` must not be any arbitrary objects except ::RString.
+ * @post `dst` has the contents of `src` appended, with encoding
+ * converted into `dst`'s one, into the end of `dst`.
+ */
+VALUE rb_str_concat(VALUE dst, VALUE src);
+
+/* random.c */
+
+/**
+ * This is a universal hash function.
+ *
+ * @warning This function changes its value per process.
+ * @param[in] ptr Target message.
+ * @param[in] len Length of `ptr` in bytes.
+ * @return A pseudorandom number suitable for Hash's hash value.
+ * @see Aumasson, JP., Bernstein, D.J., "SipHash: A Fast Short-Input
+ * PRF", In proceedings of 13th International Conference on
+ * Cryptology in India (INDOCRYPT 2012), LNCS 7668, pp. 489-508,
+ * 2012. http://doi.org/10.1007/978-3-642-34931-7_28
+*/
+st_index_t rb_memhash(const void *ptr, long len);
+
+/**
+ * Starts a series of hashing. Suppose you have a struct:
+ *
+ * ```CXX
+ * struct foo_tag {
+ * unsigned char bar;
+ * uint32_t baz;
+ * };
+ * ```
+ *
+ * It is not a wise idea to call rb_memhash() over it, because there could be
+ * padding bits. Instead you should explicitly iterate over each fields:
+ *
+ * ```CXX
+ * foo_tag foo = { 0, 0, };
+ * st_index_t hash = 0;
+ *
+ * hash = rb_hash_start(0);
+ * hash = rb_hash_uint(hash, foo.bar);
+ * hash = rb_hash_uint32(hash, foo.baz);
+ * hash = rb_hash_end(hash);
+ * ```
+ *
+ * @param[in] i Initial value.
+ * @return A hash value.
+ */
+st_index_t rb_hash_start(st_index_t i);
+
+/** @alias{st_hash_uint32} */
+#define rb_hash_uint32(h, i) st_hash_uint32((h), (i))
+
+/** @alias{st_hash_uint} */
+#define rb_hash_uint(h, i) st_hash_uint((h), (i))
+
+/** @alias{st_hash_end} */
+#define rb_hash_end(h) st_hash_end(h)
+
+/* string.c */
+
+/**
+ * Calculates a hash value of a string. This is one of the two functions that
+ * constructs struct ::st_hash_type.
+ *
+ * @param[in] str An object of ::RString.
+ * @return A hash value.
+ * @pre `str` must not be any arbitrary object except ::RString.
+ *
+ * @internal
+ *
+ * Although safe to call, there must be no particular use case of this function
+ * for extension libraries. Only ruby internals must know about it.
+ *
+ * This is not a simple alias of rb_memhash(), because it considers the passed
+ * string's encoding as well as its contents.
+ */
+st_index_t rb_str_hash(VALUE str);
+
+/**
+ * Compares two strings. This is one of the two functions that constructs
+ * struct ::st_hash_type.
+ *
+ * @param[in] str1 A string.
+ * @param[in] str2 Another string.
+ * @retval 0 They have identical contents, length, and encodings.
+ * @retval 1 Otherwise.
+ * @pre Both objects must not be any arbitrary objects except
+ * ::RString.
+ *
+ * @internal
+ *
+ * In contrast to rb_str_hash(), this could be handy for comparison that only
+ * concerns equality. rb_str_cmp() returns 1, 0, -1.
+ */
+int rb_str_hash_cmp(VALUE str1, VALUE str2);
+
+/**
+ * Checks if two strings are comparable each other or not. Because
+ * rb_str_cmp() must return "lesser than" or "greater than" information,
+ * comparing two strings needs a stricter restriction. Both sides must be in a
+ * same set of strings which have total order. This is to check that property.
+ * Intuitive it sounds? But they can have different encodings. A character
+ * and another might or might not appear in the same order in their codepoints.
+ * It is complicated than you think.
+ *
+ * @param[in] str1 A string.
+ * @param[in] str2 Another string.
+ * @retval 1 They agree on a total order.
+ * @retval 0 Otherwise.
+ * @pre Both objects must not be any arbitrary objects except
+ * ::RString.
+ */
+int rb_str_comparable(VALUE str1, VALUE str2);
+
+/**
+ * Compares two strings, as in `strcmp(3)`. This does not consider the current
+ * locale, but considers the encodings of both sides instead.
+ *
+ * @param[in] lhs A string.
+ * @param[in] rhs Another string.
+ * @retval -1 `lhs` is "bigger than" `rhs`.
+ * @retval 1 `rhs` is "bigger than" `lhs`.
+ * @retval 0 Otherwise, e.g. not comparable.
+ * @pre Both objects must not be any arbitrary objects except
+ * ::RString.
+ */
+int rb_str_cmp(VALUE lhs, VALUE rhs);
+
+/**
+ * Equality of two strings.
+ *
+ * If `str2` is not a String, it resorts to `str2 == str1`. Otherwise if they
+ * are not comparable, returns ::RUBY_Qfalse. Otherwise if they have the same
+ * contents and the length, returns ::RUBY_Qtrue. Otherwise, returns
+ * ::RUBY_Qfalse.
+ *
+ * @param[in] str1 A string.
+ * @param[in] str2 Another string.
+ * @retval RUBY_Qtrue They are equal.
+ * @retval RUBY_Qfalse They are either different, or not comparable.
+ */
+VALUE rb_str_equal(VALUE str1, VALUE str2);
+
+/**
+ * Shrinks the given string for the given number of bytes.
+ *
+ * @param[out] str String to squash.
+ * @param[in] len Number of bytes to reduce.
+ * @exception rb_eRuntimeError `str` is `locktmp`-ed.
+ * @exception rb_eFrozenError `str` is frozen.
+ * @return The passed `str`.
+ * @pre `str` must not be any arbitrary objects except ::RString.
+ * @post `str` is shrunken.
+ * @warning Can break a multibyte character in middle.
+ *
+ * @internal
+ *
+ * What if `len` is negative?
+ */
+VALUE rb_str_drop_bytes(VALUE str, long len);
+
+/**
+ * Replaces some (or all) of the contents of the given string. This is the
+ * implementation of three-argumented `String#[]=`.
+ *
+ * @param[out] dst Target string to update.
+ * @param[in] beg Offset of the affected portion.
+ * @param[in] len Length of the affected portion.
+ * @param[in] src Object to be assigned.
+ * @exception rb_eTypeError `src` has no implicit conversion to String.
+ * @exception rb_eIndexError `len` is negative, or `beg` is OOB.
+ * @exception rb_eRuntimeError `dst` is `locktmp`-ed.
+ * @exception rb_eFrozenError `dst` is frozen.
+ * @note Unlike rb_str_substr(), this function raises.
+ * @post A portion of `dst` from `beg` to `len` is the stringised
+ * representation of `src`. If that replacement string is not the
+ * same length as the portion it is replacing, `dst` will be
+ * resized accordingly.
+ */
+void rb_str_update(VALUE dst, long beg, long len, VALUE src);
+
+/**
+ * Replaces the contents of the former object with the stringised contents of
+ * the latter.
+ *
+ * @param[out] dst Destination object.
+ * @param[in] src Source object.
+ * @exception rb_eTypeError `src` has no implicit conversion to String.
+ * @exception rb_eRuntimeError `dst` is `locktmp`-ed.
+ * @exception rb_eFrozenError `dst` is frozen.
+ * @return The passed `dst`.
+ * @pre `dst` must not be any arbitrary object except ::RString.
+ * @post `dst`'s former components are abandoned. It now has the
+ * identical encoding, length, and contents to `src`.
+ */
+VALUE rb_str_replace(VALUE dst, VALUE src);
+
+/**
+ * Generates a "readable" version of the receiver.
+ *
+ * @warning The output is _insecure_. Never feed one to `eval`.
+ * @warning The output is not always in the same encoding as the given one.
+ * @warning A character might or might not be escaped, depending on the
+ * result encoding.
+ * @param[in] str String to inspect.
+ * @return Its inspection, either in default internal encoding if any, or
+ * in default external encoding otherwise.
+ * @see rb_str_dump()
+ *
+ * @internal
+ *
+ * This is a (silent) fix of an actual vulnerability feeding `inspect` output
+ * strings to `eval`:
+ * https://github.com/hiki/hiki/commit/8771a6e25198e264a2bf9dc1c102fea2cc8ff975
+ *
+ * ... and its advisory:
+ * http://hikiwiki.org/en/advisory20040712.html
+ */
+VALUE rb_str_inspect(VALUE str);
+
+/**
+ * "Inverse" of rb_eval_string(). Returns a quoted version of the string. All
+ * non-printing characters are replaced by `\uNNNN` or `\xHH` notation and all
+ * special characters are escaped. The result string is guaranteed to render a
+ * string of the same contents when passed to `eval` and friends.
+ *
+ * @param[in] str String to dump.
+ * @exception rb_eRuntimeError Too many escape sequences causes integer
+ * overflow on the length of the string.
+ * @return An US-ASCII string that includes all the necessary info to
+ * reconstruct the original string.
+ */
+VALUE rb_str_dump(VALUE str);
+
+/**
+ * Divides the given string based on the given delimiter. This is the
+ * 1-argument 0-block version of `String#split`.
+ *
+ * @param[in] str Object in question to split.
+ * @param[in] delim Delimiter, in C string.
+ * @exception rb_eTypeError `str` has no implicit conversion to String.
+ * @exception rb_eArgError `delim` is a null pointer.
+ * @return An array of strings, which are substrings of the passed `str`.
+ * If `delim` is an empty C string (i.e. `""`), `str` is split into
+ * each characters. If `delim` is a C string whose sole content is
+ * a whitespace (i.e. `" "`), `str` is split on whitespaces, with
+ * leading and trailing whitespace and runs of contiguous
+ * whitespace characters ignored. Otherwise, `str` is split
+ * according to `delim`.
+ */
+VALUE rb_str_split(VALUE str, const char *delim);
+
+/**
+ * This is a ::rb_gvar_setter_t that refutes non-string assignments.
+ *
+ * @exception rb_eTypeError Passed something non-string.
+ */
+rb_gvar_setter_t rb_str_setter;
+
+/* symbol.c */
+
+/**
+ * Identical to rb_to_symbol(), except it assumes the receiver being an
+ * instance of ::RString.
+ *
+ * @param[in] str The name of the id.
+ * @exception rb_eRuntimeError Too many symbols.
+ * @return A (possibly new) id whose value is the given `str`.
+ * @pre `str` must not be any arbitrary object except ::RString.
+ * @note These days Ruby internally has two kinds of symbols
+ * (static/dynamic). Symbols created using this function would
+ * become dynamic ones; i.e. would be garbage collected. It could
+ * be safer for you to use it than alternatives, when applicable.
+ */
+VALUE rb_str_intern(VALUE str);
+
+/* string.c */
+
+/**
+ * This is an rb_sym2str() + rb_str_dup() combo.
+ *
+ * @param[in] sym A symbol to query.
+ * @return A string duplicating the symbol's backend storage.
+ *
+ * @internal
+ *
+ * This function causes SEGV when the passed value is a static symbol that
+ * doesn't exist.
+ */
+VALUE rb_sym_to_s(VALUE sym);
+
+/**
+ * Counts the number of characters (not bytes) that are stored inside of the
+ * given string. This of course depends on its encoding. Also this function
+ * generally runs in O(n), because for instance you have to scan the entire
+ * string to know how many characters are there in a UTF-8 string.
+ *
+ * @param[in] str Target string to query.
+ * @return Its number of characters.
+ */
+long rb_str_strlen(VALUE str);
+
+/**
+ * Identical to rb_str_strlen(), except it returns the value in ::rb_cInteger.
+ *
+ * @param[in] str Target string to query.
+ * @return Its number of characters.
+ */
+VALUE rb_str_length(VALUE);
+
+/**
+ * "Inverse" of rb_str_sublen(). This function scans the contents to find the
+ * byte index that matches the character index. Generally speaking this is an
+ * `O(n)` operation. Could be slow.
+ *
+ * @param[in] str The string to scan.
+ * @param[in] pos Offset, in characters.
+ * @return Offset, in bytes.
+ */
+long rb_str_offset(VALUE str, long pos);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries the capacity of the given string.
+ *
+ * @see ::RString::capa
+ * @param[in] str String in question.
+ * @return Its capacity.
+ */
+size_t rb_str_capacity(VALUE str);
+
+/**
+ * Shortens `str` and adds three dots, an ellipsis, if it is longer than `len`
+ * characters. The length of the returned string in characters is less than or
+ * equal to `len`. If the length of `str` is less than or equal `len`, returns
+ * `str` itself. The encoding of returned string is equal to that of passed
+ * one. The class of returned string is equal to that of passed one.
+ *
+ * @param[in] str The string to shorten.
+ * @param[in] len The maximum string length.
+ * @exception rb_eIndexError `len` is negative.
+ * @retval str No need to add ellipsis.
+ * @retval otherwise A new, shortened string.
+ * @note The length is counted in characters.
+ */
+VALUE rb_str_ellipsize(VALUE str, long len);
+
+/**
+ * "Cleanses" the string. A string has its encoding and its contents. They,
+ * in practice, do not always fit. There are strings in the wild that are
+ * "broken"; include bit patterns that are not allowed by its encoding. That
+ * can happen when a user copy&pasted something bad, network input got
+ * clobbered by a middleman, cosmic rays hit the physical memory, and many more
+ * occasions. This function takes such strings, and fills the "broken" portion
+ * with the passed replacement bit pattern.
+ *
+ * This function also takes a ruby block. That is a neat way to do things, but
+ * can be annoying when the caller function want to use a block for another
+ * purpose.
+ *
+ * @param[in] str Target string to scrub.
+ * @param[in] repl Replacement string. When it is a string,
+ * this function takes that as a replacement.
+ * When it is ::RUBY_Qnil, this function tries
+ * to yield a block (if any) and takes its
+ * evaluated value as a replacement. In case
+ * of ::RUBY_Qnil without a block, this
+ * function takes an encoding-specific default
+ * character (`U+FFFD`, for instance) as a last
+ * resort.
+ * @exception rb_eTypeError `repl` is neither string nor nil.
+ * @exception rb_eArgError `repl` itself is broken.
+ * @exception rb_eEncCompatError `repl` and `str` are incompatible.
+ * @retval RUBY_Qnil `str` is already clean.
+ * @retval otherwise A new, clean string.
+ */
+VALUE rb_str_scrub(VALUE str, VALUE repl);
+
+/**
+ * Searches for the "successor" of a string. This function is complicated!
+ * This is the only function in the entire ruby API (either C or Ruby) that
+ * generates a string out of thin air. First, the successor to an empty string
+ * is a new empty string:
+ *
+ * ```ruby
+ * ''.succ # => ""
+ * ```
+ *
+ * Otherwise the successor is calculated by "incrementing" characters. The
+ * first character to be incremented is the rightmost alphanumeric: or, if no
+ * alphanumerics, the rightmost character:
+ *
+ * ```ruby
+ * 'THX1138'.succ # => "THX1139"
+ * '<<koala>>'.succ # => "<<koalb>>"
+ * '***'.succ # => '**+'
+ * ```
+ *
+ * The successor to a digit is another digit, "carrying" to the next-left
+ * character for a "rollover" from 9 to 0, and prepending another digit if
+ * necessary:
+ *
+ * ```ruby
+ * '00'.succ # => "01"
+ * '09'.succ # => "10"
+ * '99'.succ # => "100"
+ * '-9'.succ # => "-10"
+ * ```
+ *
+ * The successor to a letter is another letter of the same case, carrying to
+ * the next-left character for a rollover, and prepending another same-case
+ * letter if necessary:
+ *
+ * ```ruby
+ * 'aa'.succ # => "ab"
+ * 'az'.succ # => "ba"
+ * 'zz'.succ # => "aaa"
+ * 'AA'.succ # => "AB"
+ * 'AZ'.succ # => "BA"
+ * 'ZZ'.succ # => "AAA"
+ * ```
+ *
+ * The successor to a non-alphanumeric character is the next character in the
+ * underlying character set's collating sequence, carrying to the next-left
+ * character for a rollover, and prepending another character if necessary:
+ *
+ * ```ruby
+ * s = "\u03A1"
+ * s.succ # => "\u03A3" # There is no such thing like \u03A2.
+ * s = 255.chr * 3
+ * s # => "\xFF\xFF\xFF"
+ * s.succ # => "\x01\x00\x00\x00"
+ * ```
+ *
+ * Carrying can occur between and among mixtures of alphanumeric characters:
+ *
+ * ```ruby
+ * s = 'zz99zz99'
+ * s.succ # => "aaa00aa00"
+ * s = '99zz99zz'
+ * s.succ # => "100aa00aa"
+ * s = '1.9.9'
+ * s.succ # => "2.0.0"
+ * ```
+ *
+ * @param[in] orig Predecessor string.
+ * @return Successor string.
+ */
+VALUE rb_str_succ(VALUE orig);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[in] str A C string.
+ * @return `strlen`, casted to `long`.
+ */
+static inline long
+rbimpl_strlen(const char *str)
+{
+ return RBIMPL_CAST((long)strlen(str));
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[in] str A C string literal.
+ * @return Corresponding Ruby string.
+ */
+static inline VALUE
+rbimpl_str_new_cstr(const char *str)
+{
+ long len = rbimpl_strlen(str);
+ return rb_str_new_static(str, len);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[in] str A C string literal.
+ * @return Corresponding Ruby string.
+ */
+static inline VALUE
+rbimpl_usascii_str_new_cstr(const char *str)
+{
+ long len = rbimpl_strlen(str);
+ return rb_usascii_str_new_static(str, len);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[in] str A C string literal.
+ * @return Corresponding Ruby string.
+ */
+static inline VALUE
+rbimpl_utf8_str_new_cstr(const char *str)
+{
+ long len = rbimpl_strlen(str);
+ return rb_utf8_str_new_static(str, len);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[in] str A C string literal.
+ * @return Corresponding Ruby string.
+ */
+static inline VALUE
+rbimpl_external_str_new_cstr(const char *str)
+{
+ long len = rbimpl_strlen(str);
+ return rb_external_str_new(str, len);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[in] str A C string literal.
+ * @return Corresponding Ruby string.
+ */
+static inline VALUE
+rbimpl_locale_str_new_cstr(const char *str)
+{
+ long len = rbimpl_strlen(str);
+ return rb_locale_str_new(str, len);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[in] str A C string literal.
+ * @return Corresponding Ruby string.
+ */
+static inline VALUE
+rbimpl_str_buf_new_cstr(const char *str)
+{
+ long len = rbimpl_strlen(str);
+ VALUE buf = rb_str_buf_new(len);
+ return rb_str_buf_cat(buf, str, len);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[out] buf A string buffer.
+ * @param[in] str A C string literal.
+ * @return `buf` itself.
+ */
+static inline VALUE
+rbimpl_str_cat_cstr(VALUE buf, const char *str)
+{
+ long len = rbimpl_strlen(str);
+ return rb_str_cat(buf, str, len);
+}
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail. Don't bother.
+ *
+ * @param[in] exc An exception class.
+ * @param[in] str A C string literal.
+ * @return An instance of `exc`.
+ */
+static inline VALUE
+rbimpl_exc_new_cstr(VALUE exc, const char *str)
+{
+ long len = rbimpl_strlen(str);
+ return rb_exc_new(exc, str, len);
+}
+
+/**
+ * Allocates an instance of ::rb_cString.
+ *
+ * @param[in] str A memory region of `len` bytes length.
+ * @param[in] len Length of `ptr`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString, of `len` bytes length, of
+ * "binary" encoding, whose contents are verbatim copy of `str`.
+ * @pre At least `len` bytes of continuous memory region shall be
+ * accessible via `str`.
+ */
+#define rb_str_new(str, len) \
+ ((RBIMPL_CONSTANT_P(str) && \
+ RBIMPL_CONSTANT_P(len) ? \
+ rb_str_new_static : \
+ rb_str_new) ((str), (len)))
+
+/**
+ * Identical to #rb_str_new, except it assumes the passed pointer is a pointer
+ * to a C string.
+ *
+ * @param[in] str A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString, of "binary" encoding, whose
+ * contents are verbatim copy of `str`.
+ * @pre `str` must not be a null pointer.
+ */
+#define rb_str_new_cstr(str) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_str_new_cstr : \
+ rb_str_new_cstr) (str))
+
+/**
+ * Identical to #rb_str_new, except it generates a string of "US ASCII"
+ * encoding. This is different from rb_external_str_new(), not only for the
+ * output encoding, but also it doesn't convert the contents.
+ *
+ * @param[in] str A memory region of `len` bytes length.
+ * @param[in] len Length of `str`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString, of `len` bytes length, of
+ * "US ASCII" encoding, whose contents are verbatim copy of `str`.
+ */
+#define rb_usascii_str_new(str, len) \
+ ((RBIMPL_CONSTANT_P(str) && \
+ RBIMPL_CONSTANT_P(len) ? \
+ rb_usascii_str_new_static : \
+ rb_usascii_str_new) ((str), (len)))
+
+/**
+ * Identical to #rb_str_new, except it generates a string of "UTF-8" encoding.
+ *
+ * @param[in] str A memory region of `len` bytes length.
+ * @param[in] len Length of `str`, in bytes, not including the
+ * terminating NUL character.
+ * @exception rb_eNoMemError Failed to allocate `len+1` bytes.
+ * @exception rb_eArgError `len` is negative.
+ * @return An instance of ::rb_cString, of `len` bytes length, of
+ * "UTF-8" encoding, whose contents are verbatim copy of `str`.
+ */
+#define rb_utf8_str_new(str, len) \
+ ((RBIMPL_CONSTANT_P(str) && \
+ RBIMPL_CONSTANT_P(len) ? \
+ rb_utf8_str_new_static : \
+ rb_utf8_str_new) ((str), (len)))
+
+/**
+ * Identical to #rb_str_new_cstr, except it generates a string of "US ASCII"
+ * encoding. It can also be seen as a routine Identical to
+ * #rb_usascii_str_new, except it assumes the passed pointer is a pointer to a
+ * C string.
+ *
+ * @param[in] str A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString, of "US ASCII" encoding, whose
+ * contents are verbatim copy of `str`.
+ * @pre `str` must not be a null pointer.
+ */
+#define rb_usascii_str_new_cstr(str) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_usascii_str_new_cstr : \
+ rb_usascii_str_new_cstr) (str))
+
+/**
+ * Identical to #rb_str_new_cstr, except it generates a string of "UTF-8"
+ * encoding. It can also be seen as a routine Identical to #rb_utf8_str_new,
+ * except it assumes the passed pointer is a pointer to a C string.
+ *
+ * @param[in] str A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString, of "UTF-8" encoding, whose contents
+ * are verbatim copy of `str`.
+ * @pre `str` must not be a null pointer.
+ */
+#define rb_utf8_str_new_cstr(str) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_utf8_str_new_cstr : \
+ rb_utf8_str_new_cstr) (str))
+
+/**
+ * Identical to #rb_str_new_cstr, except it generates a string of "default
+ * external" encoding.
+ *
+ * @param[in] str A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to "default external" is fully defined over
+ * the given contents, then the return value is a string of
+ * "default external" encoding, whose contents are the converted
+ * ones. Otherwise the string is a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ * @pre `str` must not be a null pointer.
+ */
+#define rb_external_str_new_cstr(str) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_external_str_new_cstr : \
+ rb_external_str_new_cstr) (str))
+
+/**
+ * Identical to #rb_external_str_new_cstr, except it generates a string of
+ * "locale" encoding instead of "default external".
+ *
+ * @param[in] str A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString. In case encoding conversion from
+ * "default internal" to "locale" is fully defined over the given
+ * contents, then the return value is a string of "locale"
+ * encoding, whose contents are the converted ones. Otherwise the
+ * string is a junk.
+ * @warning It doesn't raise on a conversion failure and silently ends up in
+ * a corrupted output. You can know the failure by querying
+ * `valid_encoding?` of the result object.
+ * @pre `str` must not be a null pointer.
+ */
+#define rb_locale_str_new_cstr(str) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_locale_str_new_cstr : \
+ rb_locale_str_new_cstr) (str))
+
+/**
+ * Identical to #rb_str_new_cstr, except done differently.
+ *
+ * @param[in] str A C string.
+ * @exception rb_eNoMemError Failed to allocate memory.
+ * @return An instance of ::rb_cString, of "binary" encoding, whose
+ * contents are verbatim copy of `str`.
+ * @pre `str` must not be a null pointer.
+ */
+#define rb_str_buf_new_cstr(str) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_str_buf_new_cstr : \
+ rb_str_buf_new_cstr) (str))
+
+/**
+ * Identical to rb_str_cat(), except it assumes the passed pointer is a pointer
+ * to a C string.
+ *
+ * @param[out] buf Destination object.
+ * @param[in] str Contents to append.
+ * @exception rb_eArgError Result string too big.
+ * @return The passed `buf`.
+ * @pre `buf` must not be any arbitrary objects except ::RString.
+ * @pre `str` must not be a null pointer.
+ * @post `buf` has the contents of `str` appended.
+ */
+#define rb_str_cat_cstr(buf, str) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_str_cat_cstr : \
+ rb_str_cat_cstr) ((buf), (str)))
+
+/**
+ * Identical to rb_exc_new(), except it assumes the passed pointer is a pointer
+ * to a C string.
+ *
+ * @param[out] exc A subclass of ::rb_eException.
+ * @param[in] str Message to raise.
+ * @return An instance of `exc` whose message is `str`.
+ * @pre `str` must not be a null pointer.
+ */
+#define rb_exc_new_cstr(exc, str) \
+ ((RBIMPL_CONSTANT_P(str) ? \
+ rbimpl_exc_new_cstr : \
+ rb_exc_new_cstr) ((exc), (str)))
+
+#define rb_str_new2 rb_str_new_cstr /**< @old{rb_str_new_cstr} */
+#define rb_str_new3 rb_str_new_shared /**< @old{rb_str_new_shared} */
+#define rb_str_new4 rb_str_new_frozen /**< @old{rb_str_new_frozen} */
+#define rb_str_new5 rb_str_new_with_class /**< @old{rb_str_new_with_class} */
+#define rb_str_buf_new2 rb_str_buf_new_cstr /**< @old{rb_str_buf_new_cstr} */
+#define rb_usascii_str_new2 rb_usascii_str_new_cstr /**< @old{rb_usascii_str_new_cstr} */
+#define rb_str_buf_cat rb_str_cat /**< @alias{rb_str_cat} */
+#define rb_str_buf_cat2 rb_str_cat_cstr /**< @old{rb_usascii_str_new_cstr} */
+#define rb_str_cat2 rb_str_cat_cstr /**< @old{rb_str_cat_cstr} */
+
+/**
+ * Length of a string literal.
+ *
+ * @param[in] str A C String literal.
+ * @return An integer constant expression that represents the number of
+ * `str`'s elements, not including the terminating NUL character.
+ */
+#define rb_strlen_lit(str) ((sizeof(str "") / sizeof(str ""[0])) - 1)
+
+/**
+ * Identical to rb_str_new_static(), except it cannot take string variables.
+ *
+ * @param[in] str A C string literal.
+ * @pre `str` must not be a variable.
+ * @return An instance of ::rb_cString, of "binary" encoding, whose backend
+ * storage is the passed C string literal.
+ * @warning It is a very bad idea to write to a C string literal (often
+ * immediate SEGV shall occur). Consider return values of this
+ * function be read-only.
+ */
+#define rb_str_new_lit(str) rb_str_new_static((str), rb_strlen_lit(str))
+
+/**
+ * Identical to rb_usascii_str_new_static(), except it cannot take string
+ * variables.
+ *
+ * @param[in] str A C string literal.
+ * @pre `str` must not be a variable.
+ * @return An instance of ::rb_cString, of "US ASCII" encoding, whose
+ * backend storage is the passed C string literal.
+ * @warning It is a very bad idea to write to a C string literal (often
+ * immediate SEGV shall occur). Consider return values of this
+ * function be read-only.
+ */
+#define rb_usascii_str_new_lit(str) rb_usascii_str_new_static((str), rb_strlen_lit(str))
+
+/**
+ * Identical to rb_utf8_str_new_static(), except it cannot take string
+ * variables.
+ *
+ * @param[in] str A C string literal.
+ * @pre `str` must not be a variable.
+ * @return An instance of ::rb_cString, of "UTF-8" encoding, whose backend
+ * storage is the passed C string literal.
+ * @warning It is a very bad idea to write to a C string literal (often
+ * immediate SEGV shall occur). Consider return values of this
+ * function be read-only.
+ */
+#define rb_utf8_str_new_lit(str) rb_utf8_str_new_static((str), rb_strlen_lit(str))
+
+/**
+ * Identical to rb_enc_str_new_static(), except it cannot take string
+ * variables.
+ *
+ * @param[in] str A C string literal.
+ * @param[in] enc A pointer to an encoding.
+ * @pre `str` must not be a variable.
+ * @return An instance of ::rb_cString, of the passed encoding, whose
+ * backend storage is the passed C string literal.
+ * @warning It is a very bad idea to write to a C string literal (often
+ * immediate SEGV shall occur). Consider return values of this
+ * function be read-only.
+ */
+#define rb_enc_str_new_lit(str, enc) rb_enc_str_new_static((str), rb_strlen_lit(str), (enc))
+
+#define rb_str_new_literal(str) rb_str_new_lit(str) /**< @alias{rb_str_new_lit} */
+#define rb_usascii_str_new_literal(str) rb_usascii_str_new_lit(str) /**< @alias{rb_usascii_str_new_lit} */
+#define rb_utf8_str_new_literal(str) rb_utf8_str_new_lit(str) /**< @alias{rb_utf8_str_new_lit} */
+#define rb_enc_str_new_literal(str, enc) rb_enc_str_new_lit(str, enc) /**< @alias{rb_enc_str_new_lit} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_STRING_H */
diff --git a/include/ruby/internal/intern/struct.h b/include/ruby/internal/intern/struct.h
new file mode 100644
index 0000000000..16b3fad4e0
--- /dev/null
+++ b/include/ruby/internal/intern/struct.h
@@ -0,0 +1,225 @@
+#ifndef RBIMPL_INTERN_STRUCT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_STRUCT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cStruct.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/intern/vm.h" /* rb_alloc_func_t */
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* struct.c */
+
+/**
+ * Creates an instance of the given struct.
+ *
+ * @param[in] klass The class of the instance to allocate.
+ * @param[in] ... The fields.
+ * @return Allocated instance of `klass`.
+ * @pre `klass` must be a subclass of ::rb_cStruct.
+ * @note Number of variadic arguments must much that of the passed klass'
+ * fields.
+ */
+VALUE rb_struct_new(VALUE klass, ...);
+
+/**
+ * Defines a struct class.
+ *
+ * @param[in] name Name of the class.
+ * @param[in] ... Arbitrary number of `const char*`, terminated by
+ * NULL. Each of which are the name of fields.
+ * @exception rb_eNameError `name` is not a constant name.
+ * @exception rb_eTypeError `name` is already taken.
+ * @exception rb_eArgError Duplicated field name.
+ * @return The defined class.
+ * @post Global toplevel constant `name` is defined.
+ * @note `name` is allowed to be a null pointer. This function creates
+ * an anonymous struct class then.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ *
+ * @internal
+ *
+ * Not seriously checked but it seems this function does not share its
+ * implementation with how `Struct.new` is implemented...?
+ */
+VALUE rb_struct_define(const char *name, ...);
+
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * Identical to rb_struct_define(), except it defines the class under the
+ * specified namespace instead of global toplevel.
+ *
+ * @param[out] space Namespace that the defining class shall reside.
+ * @param[in] name Name of the class.
+ * @param[in] ... Arbitrary number of `const char*`, terminated by
+ * NULL. Each of which are the name of fields.
+ * @exception rb_eNameError `name` is not a constant name.
+ * @exception rb_eTypeError `name` is already taken.
+ * @exception rb_eArgError Duplicated field name.
+ * @return The defined class.
+ * @post `name` is a constant under `space`.
+ * @note In contrast to rb_struct_define(), it doesn't make any sense to
+ * pass a null pointer to this function.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ */
+VALUE rb_struct_define_under(VALUE space, const char *name, ...);
+
+/**
+ * Identical to rb_struct_new(), except it takes the field values as a Ruby
+ * array.
+ *
+ * @param[in] klass The class of the instance to allocate.
+ * @param[in] values Field values.
+ * @return Allocated instance of `klass`.
+ * @pre `klass` must be a subclass of ::rb_cStruct.
+ * @pre `values` must be an instance of struct ::RArray.
+ */
+VALUE rb_struct_alloc(VALUE klass, VALUE values);
+
+/**
+ * Mass-assigns a struct's fields.
+ *
+ * @param[out] self An instance of a struct class to squash.
+ * @param[in] values New values.
+ * @return ::RUBY_Qnil.
+ */
+VALUE rb_struct_initialize(VALUE self, VALUE values);
+
+/**
+ * Identical to rb_struct_aref(), except it takes ::ID instead of ::VALUE.
+ *
+ * @param[in] self An instance of a struct class.
+ * @param[in] key Key to query.
+ * @exception rb_eTypeError `self` is not a struct.
+ * @exception rb_eNameError No such field.
+ * @return The value stored at `key` in `self`.
+ */
+VALUE rb_struct_getmember(VALUE self, ID key);
+
+/**
+ * Queries the list of the names of the fields of the given struct class.
+ *
+ * @param[in] klass A subclass of ::rb_cStruct.
+ * @return The list of the names of the fields of `klass`.
+ */
+VALUE rb_struct_s_members(VALUE klass);
+
+/**
+ * Queries the list of the names of the fields of the class of the given struct
+ * object. This is almost the same as calling rb_struct_s_members() over the
+ * class of the receiver.
+ *
+ * @internal
+ *
+ * "Almost"? What exactly is the difference?
+ *
+ * @endinternal
+ *
+ * @param[in] self An instance of a subclass of ::rb_cStruct.
+ * @return The list of the names of the fields.
+ */
+VALUE rb_struct_members(VALUE self);
+
+/**
+ * Allocates an instance of the given class. This consequential name is of
+ * course because rb_struct_alloc() not only allocates but also initialises an
+ * instance. The API design is broken.
+ *
+ * @param[in] klass A subclass of ::rb_cStruct.
+ * @return An allocated instance of `klass`, not initialised.
+ */
+VALUE rb_struct_alloc_noinit(VALUE klass);
+
+/**
+ * Identical to rb_struct_define(), except it does not define accessor methods.
+ * You have to define them yourself. Forget about the allocator function
+ * parameter; it is for internal use only. Extension libraries are unable to
+ * properly allocate a ruby struct, because `RStruct` is opaque.
+ *
+ * @internal
+ *
+ * Several flags must be set up properly for ::RUBY_T_STRUCT objects, which are
+ * also missing for extension libraries.
+ *
+ * @endinternal
+ *
+ * @param[in] name Name of the class.
+ * @param[in] super Superclass of the defining class.
+ * @param[in] func Must be 0 for extension libraries.
+ * @param[in] ... Arbitrary number of `const char*`, terminated by
+ * NULL. Each of which are the name of fields.
+ * @exception rb_eNameError `name` is not a constant name.
+ * @exception rb_eTypeError `name` is already taken.
+ * @exception rb_eArgError Duplicated field name.
+ * @return The defined class.
+ * @post Global toplevel constant `name` is defined.
+ * @note `name` is allowed to be a null pointer. This function creates
+ * an anonymous struct class then.
+ */
+VALUE rb_struct_define_without_accessor(const char *name, VALUE super, rb_alloc_func_t func, ...);
+
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * Identical to rb_struct_define_without_accessor(), except it defines the
+ * class under the specified namespace instead of global toplevel. It can also
+ * be seen as a routine identical to rb_struct_define_under(), except it does
+ * not define accessor methods.
+ *
+ * @param[out] outer Namespace that the defining class shall reside.
+ * @param[in] class_name Name of the class.
+ * @param[in] super Superclass of the defining class.
+ * @param[in] alloc Must be 0 for extension libraries.
+ * @param[in] ... Arbitrary number of `const char*`, terminated by
+ * NULL. Each of which are the name of fields.
+ * @exception rb_eNameError `class_name` is not a constant name.
+ * @exception rb_eTypeError `class_name` is already taken.
+ * @exception rb_eArgError Duplicated field name.
+ * @return The defined class.
+ * @post `class_name` is a constant under `outer`.
+ * @note In contrast to rb_struct_define_without_accessor(), it doesn't
+ * make any sense to pass a null name.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ */
+VALUE rb_struct_define_without_accessor_under(VALUE outer, const char *class_name, VALUE super, rb_alloc_func_t alloc, ...);
+
+/**
+ * Defines an anonymous data class.
+ *
+ * @endinternal
+ *
+ * @param[in] super Superclass of the defining class. Must be a
+ * descendant of ::rb_cData, or 0 as ::rb_cData.
+ * @param[in] ... Arbitrary number of `const char*`, terminated by
+ * NULL. Each of which are the name of fields.
+ * @exception rb_eArgError Duplicated field name.
+ * @return The defined class.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ */
+VALUE rb_data_define(VALUE super, ...);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_STRUCT_H */
diff --git a/include/ruby/internal/intern/thread.h b/include/ruby/internal/intern/thread.h
new file mode 100644
index 0000000000..4d87452745
--- /dev/null
+++ b/include/ruby/internal/intern/thread.h
@@ -0,0 +1,492 @@
+#ifndef RBIMPL_INTERN_THREAD_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_THREAD_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cThread.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/config.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+struct timeval;
+
+/* thread.c */
+
+/**
+ * Tries to switch to another thread. This function blocks until the current
+ * thread re-acquires the GVL.
+ *
+ * @exception rb_eInterrupt Operation interrupted.
+ */
+void rb_thread_schedule(void);
+
+/**
+ * Blocks the current thread until the given file descriptor is ready to be
+ * read.
+ *
+ * @param[in] fd A file descriptor.
+ * @exception rb_eIOError Closed stream.
+ * @exception rb_eSystemCallError Situations like EBADF.
+ */
+int rb_thread_wait_fd(int fd);
+
+/**
+ * Identical to rb_thread_wait_fd(), except it blocks the current thread until
+ * the given file descriptor is ready to be written.
+ *
+ * @param[in] fd A file descriptor.
+ * @exception rb_eIOError Closed stream.
+ * @exception rb_eSystemCallError Situations like EBADF.
+ */
+int rb_thread_fd_writable(int fd);
+
+/**
+ * This funciton is now a no-op. It was previously used to interrupt threads
+ * that were using the given file descriptor and wait for them to finish.
+ *
+ * @deprecated Use IO with RUBY_IO_MODE_EXTERNAL and `rb_io_close` instead.
+ *
+ * @param[in] fd A file descriptor.
+ * @note This function blocks until all the threads waiting for such fd
+ * have woken up.
+ */
+void rb_thread_fd_close(int fd);
+
+/**
+ * Checks if the thread this function is running is the only thread that is
+ * currently alive.
+ *
+ * @retval 1 Yes it is.
+ * @retval 0 No it isn't.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. There are Ractors these days.
+ */
+int rb_thread_alone(void);
+
+/**
+ * Blocks for the given period of time.
+ *
+ * @warning This function can be interrupted by signals.
+ * @param[in] sec Duration in seconds.
+ * @exception rb_eInterrupt Interrupted.
+ */
+void rb_thread_sleep(int sec);
+
+/**
+ * Blocks indefinitely.
+ *
+ * @exception rb_eInterrupt Interrupted.
+ */
+void rb_thread_sleep_forever(void);
+
+/**
+ * Identical to rb_thread_sleep_forever(), except the thread calling this
+ * function is considered "dead" when our deadlock checker is triggered.
+ *
+ * @exception rb_eInterrupt Interrupted.
+ */
+void rb_thread_sleep_deadly(void);
+
+/**
+ * Stops the current thread. This is not the end of the thread's lifecycle. A
+ * stopped thread can later be woken up.
+ *
+ * @exception rb_eThreadError Stopping this thread would deadlock.
+ * @retval ::RUBY_Qnil Always.
+ *
+ * @internal
+ *
+ * The return value makes no sense at all.
+ */
+VALUE rb_thread_stop(void);
+
+/**
+ * Marks a given thread as eligible for scheduling.
+ *
+ * @note It may still remain blocked on I/O.
+ * @note This does not invoke the scheduler itself.
+ *
+ * @param[out] thread Thread in question to wake up.
+ * @exception rb_eThreadError Stop flogging a dead horse.
+ * @return The passed thread.
+ * @post The passed thread is made runnable.
+ */
+VALUE rb_thread_wakeup(VALUE thread);
+
+/**
+ * Identical to rb_thread_wakeup(), except it doesn't raise on an already
+ * killed thread.
+ *
+ * @param[out] thread A thread to wake up.
+ * @retval RUBY_Qnil `thread` is already killed.
+ * @retval otherwise `thread` is alive.
+ * @post The passed thread is made runnable, unless killed.
+ */
+VALUE rb_thread_wakeup_alive(VALUE thread);
+
+/**
+ * This is a rb_thread_wakeup() + rb_thread_schedule() combo.
+ *
+ * @note There is no guarantee that this function yields to the passed
+ * thread. It may still remain blocked on I/O.
+ * @param[out] thread Thread in question to wake up.
+ * @exception rb_eThreadError Stop flogging a dead horse.
+ * @return The passed thread.
+ */
+VALUE rb_thread_run(VALUE thread);
+
+/**
+ * Terminates the given thread. Unlike a stopped thread, a killed thread could
+ * never be revived. This function does return, when passed e.g. an already
+ * killed thread. But if the passed thread is the only one, or a special
+ * thread called "main", then it also terminates the entire process.
+ *
+ * @param[out] thread The thread to terminate.
+ * @exception rb_eFatal The passed thread is the running thread.
+ * @exception rb_eSystemExit The passed thread is the last thread.
+ * @return The passed thread.
+ * @post Either the passed thread, or the process entirely, is killed.
+ *
+ * @internal
+ *
+ * It seems killing the main thread also kills the entire process even if there
+ * are multiple running ractors. No idea why.
+ */
+VALUE rb_thread_kill(VALUE thread);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Creates a Ruby thread that is backended by a C function.
+ *
+ * @param[in] f The function to run on a thread.
+ * @param[in,out] g Passed through to `f`.
+ * @exception rb_eThreadError Could not create a ruby thread.
+ * @exception rb_eSystemCallError Situations like `EPERM`.
+ * @return Allocated instance of ::rb_cThread.
+ * @note This doesn't wait for anything.
+ */
+VALUE rb_thread_create(VALUE (*f)(void *g), void *g);
+
+/**
+ * Identical to rb_thread_sleep(), except it takes struct `timeval` instead.
+ *
+ * @warning This function can be interrupted by signals.
+ * @param[in] time Duration.
+ * @exception rb_eInterrupt Interrupted.
+ */
+void rb_thread_wait_for(struct timeval time);
+
+/**
+ * Obtains the "current" thread.
+ *
+ * @return The current thread of the current ractor of the current execution
+ * context.
+ * @pre This function must be called from a thread controlled by ruby.
+ */
+VALUE rb_thread_current(void);
+
+/**
+ * Obtains the "main" thread. There are threads called main. Historically the
+ * (only) main thread was the one which runs when the process boots. Now that
+ * we have Ractor, there are more than one main threads.
+ *
+ * @return The main thread of the current ractor of the current execution
+ * context.
+ * @pre This function must be called from a thread controlled by ruby.
+ */
+VALUE rb_thread_main(void);
+
+/**
+ * This badly named function reads from a Fiber local storage. When this
+ * function was born there was no such thing like a Fiber. The world was
+ * innocent. But now... This is a Fiber local storage. Sorry.
+ *
+ * @param[in] thread Thread that the target Fiber is running.
+ * @param[in] key The name of the Fiber local storage to read.
+ * @retval RUBY_Qnil No such storage.
+ * @retval otherwise The value stored at `key`.
+ * @note There in fact are "true" thread local storage, but Ruby doesn't
+ * provide any interface of them to you, C programmers.
+ */
+VALUE rb_thread_local_aref(VALUE thread, ID key);
+
+/**
+ * This badly named function writes to a Fiber local storage. When this
+ * function was born there was no such thing like a Fiber. The world was
+ * innocent. But now... This is a Fiber local storage. Sorry.
+ *
+ * @param[in] thread Thread that the target Fiber is running.
+ * @param[in] key The name of the Fiber local storage to write.
+ * @param[in] val The new value of the storage.
+ * @exception rb_eFrozenError `thread` is frozen.
+ * @return The passed `val` as-is.
+ * @post Fiber local storage `key` has value of `val`.
+ * @note There in fact are "true" thread local storage, but Ruby doesn't
+ * provide any interface of them to you, C programmers.
+ */
+VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val);
+
+/**
+ * A `pthread_atfork(3posix)`-like API. Ruby expects its child processes to
+ * call this function at the very beginning of their processes. If you plan to
+ * fork a process don't forget to call it.
+ */
+void rb_thread_atfork(void);
+
+/**
+ * :FIXME: situation of this function is unclear. It seems nobody uses it.
+ * Maybe a good idea to KonMari.
+ */
+void rb_thread_atfork_before_exec(void);
+
+/**
+ * "Recursion" API entry point. This basically calls the given function with
+ * the given arguments, but additionally with recursion flag. The flag is set
+ * to 1 if the execution have already experienced the passed `g` parameter
+ * before.
+ *
+ * @param[in] f The function that possibly recurs.
+ * @param[in,out] g Passed as-is to `f`.
+ * @param[in,out] h Passed as-is to `f`.
+ * @return The return value of f.
+ */
+VALUE rb_exec_recursive(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h);
+
+/**
+ * Identical to rb_exec_recursive(), except it checks for the recursion on the
+ * ordered pair of `{ g, p }` instead of just `g`.
+ *
+ * @param[in] f The function that possibly recurs.
+ * @param[in,out] g Passed as-is to `f`.
+ * @param[in] p Paired object for recursion detection.
+ * @param[in,out] h Passed as-is to `f`.
+ */
+VALUE rb_exec_recursive_paired(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h);
+
+/**
+ * Identical to rb_exec_recursive(), except it calls `f` for outermost
+ * recursion only. Inner recursions yield calls to rb_throw_obj().
+ *
+ * @param[in] f The function that possibly recurs.
+ * @param[in,out] g Passed as-is to `f`.
+ * @param[in,out] h Passed as-is to `f`.
+ * @return The return value of f.
+ *
+ * @internal
+ *
+ * It seems nobody uses the "it calls rb_throw_obj()" part of this function.
+ * @shyouhei doesn't understand the needs.
+ */
+VALUE rb_exec_recursive_outer(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h);
+
+/**
+ * Identical to rb_exec_recursive_outer(), except it checks for the recursion
+ * on the ordered pair of `{ g, p }` instead of just `g`. It can also be seen
+ * as a routine identical to rb_exec_recursive_paired(), except it calls `f`
+ * for outermost recursion only. Inner recursions yield calls to
+ * rb_throw_obj().
+ *
+ * @param[in] f The function that possibly recurs.
+ * @param[in,out] g Passed as-is to `f`.
+ * @param[in] p Paired object for recursion detection.
+ * @param[in,out] h Passed as-is to `f`.
+ *
+ * @internal
+ *
+ * It seems nobody uses the "it calls rb_throw_obj()" part of this function.
+ * @shyouhei doesn't understand the needs.
+ */
+VALUE rb_exec_recursive_paired_outer(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h);
+
+/**
+ * This is the type of UBFs. An UBF is a function that unblocks a blocking
+ * region. For instance when a thread is blocking due to `pselect(3posix)`, it
+ * is highly expected that `pthread_kill(3posix)` can interrupt the system call
+ * and the thread could revive. Or when a thread is blocking due to
+ * `waitpid(3posix)`, it is highly expected that killing the waited process
+ * should suffice. An UBF is a function that does such things. Designing your
+ * own UBF needs deep understanding of why your blocking region blocks, how
+ * threads work in ruby, and a matter of luck. It often is the case you simply
+ * cannot cancel something that had already begun.
+ *
+ * @see rb_thread_call_without_gvl()
+ */
+typedef void rb_unblock_function_t(void *);
+
+/**
+ * @private
+ *
+ * This is an implementation detail. Must be a mistake to be here.
+ *
+ * @internal
+ *
+ * Why is this function type different from what rb_thread_call_without_gvl()
+ * takes?
+ */
+typedef VALUE rb_blocking_function_t(void *);
+
+/**
+ * Checks for interrupts. In ruby, signals are masked by default. You can
+ * call this function at will to check if there are pending signals. In case
+ * there are, they would be handled in this function.
+ *
+ * If your extension library has a function that takes a long time, consider
+ * calling it periodically.
+ *
+ * @note It might switch to another thread.
+ */
+void rb_thread_check_ints(void);
+
+/**
+ * Checks if the thread's execution was recently interrupted. If called from
+ * that thread, this function can be used to detect spurious wake-ups.
+ *
+ * @param[in] thval Thread in question.
+ * @retval 0 The thread was not interrupted.
+ * @retval otherwise The thread was interrupted recently.
+ *
+ * @internal
+ *
+ * Above description is not a lie. But actually the return value is an opaque
+ * trap vector. If you know which bit means which, you can know what happened.
+ */
+int rb_thread_interrupted(VALUE thval);
+
+/**
+ * A special UBF for blocking IO operations. You need deep understanding of
+ * what this actually do before using. Basically you should not use it from
+ * extension libraries. It is too easy to mess up.
+ */
+#define RUBY_UBF_IO RBIMPL_CAST((rb_unblock_function_t *)-1)
+
+/**
+ * A special UBF for blocking process operations. You need deep understanding
+ * of what this actually do before using. Basically you should not use it from
+ * extension libraries. It is too easy to mess up.
+ */
+#define RUBY_UBF_PROCESS RBIMPL_CAST((rb_unblock_function_t *)-1)
+
+/* thread_sync.c */
+
+/**
+ * Creates a mutex.
+ *
+ * @return An allocated instance of rb_cMutex.
+ */
+VALUE rb_mutex_new(void);
+
+/**
+ * Queries if there are any threads that holds the lock.
+ *
+ * @param[in] mutex The mutex in question.
+ * @retval RUBY_Qtrue The mutex is locked by someone.
+ * @retval RUBY_Qfalse The mutex is not locked by anyone.
+ */
+VALUE rb_mutex_locked_p(VALUE mutex);
+
+/**
+ * Attempts to lock the mutex, without waiting for other threads to unlock it.
+ * Failure in locking the mutex can be detected by the return value.
+ *
+ * @param[out] mutex The mutex to lock.
+ * @retval RUBY_Qtrue Successfully locked by the current thread.
+ * @retval RUBY_Qfalse Otherwise.
+ * @note This function also returns ::RUBY_Qfalse when the mutex is
+ * already owned by the calling thread itself.
+ */
+VALUE rb_mutex_trylock(VALUE mutex);
+
+/**
+ * Attempts to lock the mutex. It waits until the mutex gets available.
+ *
+ * @param[out] mutex The mutex to lock.
+ * @exception rb_eThreadError Recursive deadlock situation.
+ * @return The passed mutex.
+ * @post The mutex is owned by the current thread.
+ */
+VALUE rb_mutex_lock(VALUE mutex);
+
+/**
+ * Releases the mutex.
+ *
+ * @param[out] mutex The mutex to unlock.
+ * @exception rb_eThreadError The mutex is not owned by the current thread.
+ * @return The passed mutex.
+ * @post Upon successful return the passed mutex is no longer owned by
+ * the current thread.
+ */
+VALUE rb_mutex_unlock(VALUE mutex);
+
+/**
+ * Releases the lock held in the mutex and waits for the period of time;
+ * reacquires the lock on wakeup.
+ *
+ * @pre The lock has to be owned by the current thread beforehand.
+ * @param[out] self The target mutex.
+ * @param[in] timeout Duration, in seconds, in ::rb_cNumeric.
+ * @exception rb_eArgError `timeout` is negative.
+ * @exception rb_eRangeError `timeout` is out of range of `time_t`.
+ * @exception rb_eThreadError The mutex is not owned by the current thread.
+ * @return Number of seconds it actually slept.
+ * @warning It is a failure not to check the return value. This function
+ * can return spuriously for various reasons. Maybe other threads
+ * can rb_thread_wakeup(). Maybe an end user can press the
+ * Control and C key from the interactive console. On the other
+ * hand it can also take longer than the specified. The mutex
+ * could be locked by someone else. It waits then.
+ * @post Upon successful return the passed mutex is owned by the current
+ * thread.
+ *
+ * @internal
+ *
+ * This function is called from `ConditionVariable#wait`. So it is not a
+ * deprecated feature. However @shyouhei have never seen any similar mutex
+ * primitive available in any other languages than Ruby.
+ *
+ * EDIT: In 2021, @shyouhei asked @ko1 in person about this API. He answered
+ * that it is his invention. The motivation behind its design is to eliminate
+ * needs of condition variables as primitives. Unlike other languages, Ruby's
+ * `ConditionVariable` class was written in pure-Ruby initially. We don't have
+ * to implement machine-native condition variables in assembly each time we
+ * port Ruby to a new architecture. This function made it possible. "I felt I
+ * was a genius when this idea came to me", said @ko1.
+ *
+ * `rb_cConditionVariable` is now written in C for speed, though.
+ */
+VALUE rb_mutex_sleep(VALUE self, VALUE timeout);
+
+/**
+ * Obtains the lock, runs the passed function, and releases the lock when it
+ * completes.
+ *
+ * @param[out] mutex The mutex to lock.
+ * @param[in] func What to do during the mutex is locked.
+ * @param[in,out] arg Passed as-is to `func`.
+ */
+VALUE rb_mutex_synchronize(VALUE mutex, VALUE (*func)(VALUE arg), VALUE arg);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_THREAD_H */
diff --git a/include/ruby/internal/intern/time.h b/include/ruby/internal/intern/time.h
new file mode 100644
index 0000000000..df482862eb
--- /dev/null
+++ b/include/ruby/internal/intern/time.h
@@ -0,0 +1,161 @@
+#ifndef RBIMPL_INTERN_TIME_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_TIME_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to ::rb_cTime.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef HAVE_TIME_H
+# include <time.h> /* for time_t */
+#endif
+
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+struct timespec;
+struct timeval;
+
+/* time.c */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Fills the current time into the given struct.
+ *
+ * @param[out] ts Return buffer.
+ * @exception rb_eSystemCallError Access denied for hardware clock.
+ * @post Current time is stored in `*ts`.
+ */
+void rb_timespec_now(struct timespec *ts);
+
+/**
+ * Creates an instance of ::rb_cTime with the given time and the local
+ * timezone.
+ *
+ * @param[in] sec Seconds since the UNIX epoch.
+ * @param[in] usec Subsecond part, in microseconds resolution.
+ * @exception rb_eRangeError Cannot express the time.
+ * @return An allocated instance of ::rb_cTime.
+ */
+VALUE rb_time_new(time_t sec, long usec);
+
+/**
+ * Identical to rb_time_new(), except it accepts the time in nanoseconds
+ * resolution.
+ *
+ * @param[in] sec Seconds since the UNIX epoch.
+ * @param[in] nsec Subsecond part, in nanoseconds resolution.
+ * @exception rb_eRangeError Cannot express the time.
+ * @return An allocated instance of ::rb_cTime.
+ */
+VALUE rb_time_nano_new(time_t sec, long nsec);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Creates an instance of ::rb_cTime, with given time and offset.
+ *
+ * @param[in] ts Time specifier.
+ * @param[in] offset Offset specifier, can take following values:
+ * - `INT_MAX`: `ts` is in local time.
+ * - `INT_MAX - 1`: `ts` is in UTC.
+ * - `-86400` to `86400`: fixed timezone.
+ * @exception rb_eArgError Malformed `offset`.
+ * @return An allocated instance of ::rb_cTime.
+ */
+VALUE rb_time_timespec_new(const struct timespec *ts, int offset);
+
+/**
+ * Identical to rb_time_timespec_new(), except it takes Ruby values instead of
+ * C structs.
+ *
+ * @param[in] timev Something numeric. Currently Integers, Rationals,
+ * and Floats are accepted.
+ * @param[in] off Offset specifier. As of 2.7 this argument is
+ * heavily extended to take following kinds of
+ * objects:
+ * - ::RUBY_Qundef ... means UTC.
+ * - ::rb_cString ... "+12:34" etc.
+ * - A mysterious "zone" object. This is largely
+ * undocumented. However the initial intent was
+ * that we want to accept
+ * `ActiveSupport::TimeZone` here. Other gems
+ * could also be possible... But how to make an
+ * acceptable class is beyond this document.
+ * @exception rb_eArgError Malformed `off`.
+ * @return An allocated instance of ::rb_cTime.
+ */
+VALUE rb_time_num_new(VALUE timev, VALUE off);
+
+/**
+ * Creates a "time interval". This basically converts an instance of
+ * ::rb_cNumeric into a struct `timeval`, but for instance negative time
+ * interval must not exist.
+ *
+ * @param[in] num An instance of ::rb_cNumeric.
+ * @exception rb_eArgError `num` is negative.
+ * @exception rb_eRangeError `num` is out of range of `timeval::tv_sec`.
+ * @return A struct that represents the identical time to `num`.
+ */
+struct timeval rb_time_interval(VALUE num);
+
+/**
+ * Converts an instance of rb_cTime to a struct timeval that represents the
+ * identical point of time. It can also take something numeric; would consider
+ * it as a UNIX time then.
+ *
+ * @param[in] time Instance of either ::rb_cTime or ::rb_cNumeric.
+ * @exception rb_eRangeError `time` is out of range of `timeval::tv_sec`.
+ * @return A struct that represents the identical time to `num`.
+ */
+struct timeval rb_time_timeval(VALUE time);
+
+/**
+ * Identical to rb_time_timeval(), except for return type.
+ *
+ * @param[in] time Instance of either ::rb_cTime or ::rb_cNumeric.
+ * @exception rb_eRangeError `time` is out of range of `timeval::tv_sec`.
+ * @return A struct that represents the identical time to `num`.
+ */
+struct timespec rb_time_timespec(VALUE time);
+
+/**
+ * Identical to rb_time_interval(), except for return type.
+ *
+ * @param[in] num An instance of ::rb_cNumeric.
+ * @exception rb_eArgError `num` is negative.
+ * @exception rb_eRangeError `num` is out of range of `timespec::tv_sec`.
+ * @return A struct that represents the identical time to `num`.
+ */
+struct timespec rb_time_timespec_interval(VALUE num);
+
+/**
+ * Queries the offset, in seconds between the time zone of the time and the
+ * UTC.
+ *
+ * @param[in] time An instance of ::rb_cTime.
+ * @return Numeric offset.
+ */
+VALUE rb_time_utc_offset(VALUE time);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_TIME_H */
diff --git a/include/ruby/internal/intern/variable.h b/include/ruby/internal/intern/variable.h
new file mode 100644
index 0000000000..479c3950c1
--- /dev/null
+++ b/include/ruby/internal/intern/variable.h
@@ -0,0 +1,628 @@
+#ifndef RBIMPL_INTERN_VARIABLE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_VARIABLE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to names inside of a Ruby program.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/st.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* variable.c */
+
+/**
+ * Queries the name of a module.
+ *
+ * @param[in] mod An instance of ::rb_cModule.
+ * @retval RUBY_Qnil `mod` is anonymous.
+ * @retval otherwise `mod` is onymous.
+ */
+VALUE rb_mod_name(VALUE mod);
+
+/**
+ * Identical to rb_mod_name(), except it returns `#<Class: ...>` style
+ * inspection for anonymous modules.
+ *
+ * @param[in] mod An instance of ::rb_cModule.
+ * @return An instance of ::rb_cString representing `mod`'s path.
+ */
+VALUE rb_class_path(VALUE mod);
+
+/**
+ * @alias{rb_mod_name}
+ *
+ * @internal
+ *
+ * Am I missing something? Why we have the same thing in different names?
+ */
+VALUE rb_class_path_cached(VALUE mod);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Names a class.
+ *
+ * @param[out] klass Target module to name.
+ * @param[out] space Namespace that `klass` shall reside.
+ * @param[in] name Name of `klass`.
+ * @post `klass` has `space::klass` name.
+ */
+void rb_set_class_path(VALUE klass, VALUE space, const char *name);
+
+/**
+ * Identical to rb_set_class_path(), except it accepts the name as Ruby's
+ * string instead of C's.
+ *
+ * @param[out] klass Target module to name.
+ * @param[out] space Namespace that `klass` shall reside.
+ * @param[in] name Name of `klass`.
+ * @post `klass` has `space::klass` name.
+ */
+void rb_set_class_path_string(VALUE klass, VALUE space, VALUE name);
+
+/**
+ * Identical to rb_path2class(), except it accepts the path as Ruby's string
+ * instead of C's.
+ *
+ * @param[in] path Path to query.
+ * @exception rb_eArgError No such constant.
+ * @exception rb_eTypeError The path resolved to a non-module.
+ * @return Resolved class.
+ */
+VALUE rb_path_to_class(VALUE path);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Resolves a `Q::W::E::R`-style path string to the actual class it points.
+ *
+ * @param[in] path Path to query.
+ * @exception rb_eArgError No such constant.
+ * @exception rb_eTypeError The path resolved to a non-module.
+ * @return Resolved class.
+ */
+VALUE rb_path2class(const char *path);
+
+/**
+ * Queries the name of the given object's class.
+ *
+ * @param[in] obj Arbitrary object.
+ * @return An instance of ::rb_cString representing `obj`'s class' path.
+ */
+VALUE rb_class_name(VALUE obj);
+
+/**
+ * Kicks the autoload procedure as if it was "touched".
+ *
+ * @param[out] space Namespace where autoload is defined.
+ * @param[in] name Name of the autoloaded constant.
+ * @retval RUBY_Qfalse No such autoload.
+ * @retval RUBY_Qtrue Autoload successfully initiated.
+ * @note As an autoloaded library is expected to define `space::name`,
+ * it is a nature of this function to have process-global side
+ * effects.
+ * @note Multiple threads can simultaneously call this API. It blocks
+ * then. That must not last indefinitely but can take longer than
+ * you expect.
+ *
+ * @internal
+ *
+ * @shyouhei has no idea why extension libraries should use this API.
+ */
+VALUE rb_autoload_load(VALUE space, ID name);
+
+/**
+ * Queries if an autoload is defined at a point.
+ *
+ * @param[in] space Namespace where autoload is defined.
+ * @param[in] name Name of the autoloaded constant.
+ * @retval RUBY_Qnil No such autoload.
+ * @retval otherwise The feature (path) registered at `space::name`.
+ */
+VALUE rb_autoload_p(VALUE space, ID name);
+
+/**
+ * Traces a global variable.
+ *
+ * @param[in] argc Either 1 or 2.
+ * @param[in] argv Variable name, optionally a Proc.
+ * @retval RUBY_Qnil No previous tracers.
+ * @retval otherwise Previous tracers.
+ *
+ * @internal
+ *
+ * @shyouhei has no idea why extension libraries should use this API.
+ */
+VALUE rb_f_trace_var(int argc, const VALUE *argv);
+
+/**
+ * Deletes the passed tracer from the passed global variable, or if omitted,
+ * deletes everything.
+ *
+ * @param[in] argc Either 1 or 2.
+ * @param[in] argv Variable name, optionally a Proc.
+ * @retval RUBY_Qnil No previous tracers.
+ * @retval otherwise Deleted tracers.
+ *
+ * @internal
+ *
+ * @shyouhei has no idea why extension libraries should use this API.
+ */
+VALUE rb_f_untrace_var(int argc, const VALUE *argv);
+
+/**
+ * Queries the list of global variables.
+ *
+ * @return The list of the name of the global variables.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+VALUE rb_f_global_variables(void);
+
+/**
+ * Aliases a global variable. Did you know that you can alias a global
+ * variable? It is like aliasing methods:
+ *
+ * ```ruby
+ * alias $dst $src
+ * ```
+ *
+ * This C function does the same thing.
+ *
+ * @param[in] dst Destination name.
+ * @param[in] src Source name.
+ * @post A global variable named `dst` is defined to be an alias of a
+ * global variable named `src`.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+void rb_alias_variable(ID dst, ID src);
+
+/**
+ * Frees the list of instance variables. 3rd parties need not know, but there
+ * are several ways to store an object's instance variables, depending on its
+ * internal structure. This function makes sense when the passed objects is
+ * using so-called "generic" backend storage. People need not be aware of this
+ * working behind-the-scenes.
+ *
+ * @param[out] obj The object in question.
+ *
+ * @internal
+ *
+ * This just destroys the given object. @shyouhei has no idea why extension
+ * libraries should use this API.
+ */
+void rb_free_generic_ivar(VALUE obj);
+
+/**
+ * Identical to rb_iv_get(), except it accepts the name as an ::ID instead of a
+ * C string.
+ *
+ * @param[in] obj Target object.
+ * @param[in] name Target instance variable to query.
+ * @retval RUBY_nil No such instance variable.
+ * @retval otherwise The value assigned to the instance variable.
+ */
+VALUE rb_ivar_get(VALUE obj, ID name);
+
+/**
+ * Identical to rb_iv_set(), except it accepts the name as an ::ID instead of a
+ * C string.
+ *
+ * @param[out] obj Target object.
+ * @param[in] name Target instance variable.
+ * @param[in] val Value to assign.
+ * @exception rb_eFrozenError Can't modify `obj`.
+ * @exception rb_eArgError `obj` has too many instance variables.
+ * @return Passed value.
+ * @post An instance variable named `name` is defined if absent on
+ * `obj`, whose value is set to `val`.
+ */
+VALUE rb_ivar_set(VALUE obj, ID name, VALUE val);
+
+/**
+ * Queries if the instance variable is defined at the object. This roughly
+ * resembles `defined?(@name)` in `obj`'s context.
+ *
+ * @param[in] obj Target object.
+ * @param[in] name Target instance variable to query.
+ * @retval RUBY_Qtrue There is an instance variable.
+ * @retval RUBY_Qfalse No such instance variable.
+ */
+VALUE rb_ivar_defined(VALUE obj, ID name);
+
+/**
+ * Iterates over an object's instance variables.
+ *
+ * @param[in] obj Target object.
+ * @param[in] func Callback function.
+ * @param[in] arg Passed as-is to the last argument of `func`.
+ */
+void rb_ivar_foreach(VALUE obj, int (*func)(ID name, VALUE val, st_data_t arg), st_data_t arg);
+
+/**
+ * Number of instance variables defined on an object.
+ *
+ * @param[in] obj Target object.
+ * @return Number of instance variables defined on `obj`.
+ */
+st_index_t rb_ivar_count(VALUE obj);
+
+/**
+ * Identical to rb_ivar_get()
+ *
+ * @param[in] obj Target object.
+ * @param[in] name Target instance variable to query.
+ * @retval RUBY_nil No such instance variable.
+ * @retval otherwise The value assigned to the instance variable.
+ *
+ * @internal
+ *
+ * Am I missing something? Why we have the same thing in different names?
+ */
+VALUE rb_attr_get(VALUE obj, ID name);
+
+/**
+ * Resembles `Object#instance_variables`.
+ *
+ * @param[in] obj Target object to query.
+ * @return An array of instance variable names for the receiver.
+ * @note Simply defining an accessor does not create the corresponding
+ * instance variable.
+ */
+VALUE rb_obj_instance_variables(VALUE obj);
+
+/**
+ * Resembles `Object#remove_instance_variable`.
+ *
+ * @param[out] obj Target object.
+ * @param[in] name Variable name to remove, either in Symbol or String.
+ * @return What was removed.
+ * @pre Instance variable named `name` is deleted from `obj`.
+ */
+VALUE rb_obj_remove_instance_variable(VALUE obj, VALUE name);
+
+/**
+ * This API is mysterious. It has been there since the initial revision. No
+ * single bits of documents has ever been written. The function name doesn't
+ * describe anything. What should be passed to the argument, or what should be
+ * the return value, are not obvious. Yet it has evolved over time. The
+ * source code is written in counter-intuitive way (as of 3.0).
+ *
+ * Simply put, don't try to understand this API.
+ */
+void *rb_mod_const_at(VALUE, void*);
+
+/**
+ * This is a variant of rb_mod_const_at(). As a result, it is also mysterious.
+ * It _seems_ it iterates over the ancestry tree of the module. But what that
+ * means is beyond a human brain.
+ */
+void *rb_mod_const_of(VALUE, void*);
+
+/**
+ * This is another mysterious API that comes with no documents at all. It
+ * seems it expects some specific data structure for the passed pointer. But
+ * the details has never been made explicit. It seems nobody should use this
+ * API.
+ */
+VALUE rb_const_list(void*);
+
+/**
+ * Resembles `Module#constants`. List up the constants defined at the
+ * receiver. This includes the names of constants in any included modules,
+ * unless `argv[0]` is ::RUBY_Qfalse.
+ *
+ * The implementation makes no guarantees about the order in which the
+ * constants are yielded.
+ *
+ * @param[in] argc Either 0 or 1.
+ * @param[in] argv Pointer to ::RUBY_Qfalse, if `argc == 1`.
+ * @param[in] recv Target namespace.
+ * @return An array of symbols, which are constant names under `recv`.
+ */
+VALUE rb_mod_constants(int argc, const VALUE *argv, VALUE recv);
+
+/**
+ * Resembles `Module#remove_const`.
+ *
+ * @param[out] space Target namespace.
+ * @param[in] name Variable name to remove, either in Symbol or String.
+ * @return What was removed.
+ * @pre Constant named `space::name` is deleted.
+ * @note In case what was removed was in fact a module or a class, this
+ * operation does not affect its name. Which means when people
+ * for instance look at it using `p` etc., it still introduces
+ * itself using the deleted name. Can confuse people.
+ */
+VALUE rb_mod_remove_const(VALUE space, VALUE name);
+
+/**
+ * Queries if the constant is defined at the namespace.
+ *
+ * @param[in] space Target namespace.
+ * @param[in] name Target name to query.
+ * @retval RUBY_Qtrue There is a constant.
+ * @retval RUBY_Qfalse No such constant.
+ *
+ * @internal
+ *
+ * The return values are not typo! This function returns ruby values casted to
+ * `int`. Completely brain-damaged design.
+ */
+int rb_const_defined(VALUE space, ID name);
+
+/**
+ * Identical to rb_const_defined(), except it doesn't look for parent classes.
+ * For instance `Array` is a toplevel constant, which is visible from
+ * everywhere. But this function does not take such things into account. It
+ * concerns only what is directly defined inside of the given namespace.
+ *
+ * @param[in] space Target namespace.
+ * @param[in] name Target name to query.
+ * @retval RUBY_Qtrue There is a constant.
+ * @retval RUBY_Qfalse No such constant.
+ *
+ * @internal
+ *
+ * The return values are not typo! This function returns ruby values casted to
+ * `int`. Completely brain-damaged design.
+ */
+int rb_const_defined_at(VALUE space, ID name);
+
+/**
+ * Identical to rb_const_defined(), except it returns false for private
+ * constants.
+ *
+ * @param[in] space Target namespace.
+ * @param[in] name Target name to query.
+ * @retval RUBY_Qtrue There is a constant.
+ * @retval RUBY_Qfalse No such constant.
+ *
+ * @internal
+ *
+ * What does "from" mean? The name sounds quite cryptic.
+ *
+ * The return values are not typo! This function returns ruby values casted to
+ * `int`. Completely brain-damaged design.
+ */
+int rb_const_defined_from(VALUE space, ID name);
+
+/**
+ * Identical to rb_const_defined(), except it returns the actual defined value.
+ *
+ * @param[in] space Target namespace.
+ * @param[in] name Target name to query.
+ * @exception rb_eNameError No such constant.
+ * @return The defined constant.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+VALUE rb_const_get(VALUE space, ID name);
+
+/**
+ * Identical to rb_const_defined_at(), except it returns the actual defined
+ * value. It can also be seen as a routine identical to rb_const_get(), except
+ * it doesn't look for parent classes.
+ *
+ * @param[in] space Target namespace.
+ * @param[in] name Target name to query.
+ * @exception rb_eNameError No such constant.
+ * @return The defined constant.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+VALUE rb_const_get_at(VALUE space, ID name);
+
+/**
+ * Identical to rb_const_defined_at(), except it returns the actual defined
+ * value. It can also be seen as a routine identical to rb_const_get(), except
+ * it doesn't return a private constant.
+ *
+ * @param[in] space Target namespace.
+ * @param[in] name Target name to query.
+ * @exception rb_eNameError No such constant.
+ * @return The defined constant.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+VALUE rb_const_get_from(VALUE space, ID name);
+
+/**
+ * Names a constant.
+ *
+ * @param[out] space Target namespace.
+ * @param[in] name Target name to query.
+ * @param[in] val Value to define.
+ * @exception rb_eTypeError `space` is not a module.
+ * @post `name` is a constant under `space`, whose value is `val`.
+ * @note You can reassign.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+void rb_const_set(VALUE space, ID name, VALUE val);
+
+/**
+ * Identical to rb_mod_remove_const(), except it takes the name as ::ID instead
+ * of ::VALUE.
+ *
+ * @param[out] space Target namespace.
+ * @param[in] name Variable name to remove, either in Symbol or String.
+ * @return What was removed.
+ * @pre Constant named `space::name` is deleted.
+ * @note In case what was removed was in fact a module or a class, this
+ * operation does not affect its name. Which means when people
+ * for instance look at it using `p` etc., it still introduces
+ * itself using the deleted name. Can confuse people.
+ */
+VALUE rb_const_remove(VALUE space, ID name);
+
+#if 0 /* EXPERIMENTAL: remove if no problem */
+RBIMPL_ATTR_NORETURN()
+/**
+ * This is the default implementation of `Module#const_missing`.
+ *
+ * @param[in] space Target namespace.
+ * @param[in] name Target name that is nonexistent.
+ * @exception rb_eNameError Always.
+ */
+VALUE rb_mod_const_missing(VALUE space, VALUE name);
+#endif
+
+/**
+ * Queries if the given class has the given class variable.
+ *
+ * @param[in] klass Target class.
+ * @param[in] name Name to query.
+ * @return RUBY_Qtrue Yes there is.
+ * @return RUBY_Qfalse No there isn't.
+ * @pre `klass` must be an instance of rb_cModule.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+VALUE rb_cvar_defined(VALUE klass, ID name);
+
+/**
+ * Assigns a value to a class variable.
+ *
+ * @param[out] klass Target class.
+ * @param[in] name Variable name.
+ * @param[in] val Value to be assigned.
+ * @post `klass` has a class variable named `name` whose value is `val`.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+void rb_cvar_set(VALUE klass, ID name, VALUE val);
+
+/**
+ * Obtains a value from a class variable.
+ *
+ * @param[in] klass Target class.
+ * @param[in] name Variable name.
+ * @exception rb_eNameError Uninitialised class variable.
+ * @exception rb_eRuntimeError `[Bug#14541]` situation.
+ * @return Class variable named `name` under `klass`.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+VALUE rb_cvar_get(VALUE klass, ID name);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_cvar_get(), except it takes additional "front" pointer.
+ * This extra parameter is a buffer, which will have the class where the
+ * queried class variable actually resides.
+ *
+ * @param[in] klass Target class.
+ * @param[in] name Variable name.
+ * @param[out] front Return buffer.
+ * @exception rb_eNameError Uninitialised class variable.
+ * @exception rb_eRuntimeError `[Bug#14541]` situation.
+ * @return Class variable named `name` under `klass`.
+ * @post `front` has the class object, which is an ancestor of `klass`,
+ * where the queried class variable actually resides.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+VALUE rb_cvar_find(VALUE klass, ID name, VALUE *front);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_cvar_set(), except it accepts C's string instead of ::ID.
+ *
+ * @param[out] klass Target class.
+ * @param[in] name Variable name.
+ * @param[in] val Value to be assigned.
+ * @post `klass` has a class variable named `name` whose value is `val`.
+ */
+void rb_cv_set(VALUE klass, const char *name, VALUE val);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_cvar_get(), except it accepts C's string instead of ::ID.
+ *
+ * @param[in] klass Target class.
+ * @param[in] name Variable name.
+ * @exception rb_eNameError Uninitialised class variable.
+ * @exception rb_eRuntimeError `[Bug#14541]` situation.
+ * @return Class variable named `name` under `klass`.
+ */
+VALUE rb_cv_get(VALUE klass, const char *name);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @alias{rb_cv_set}
+ *
+ * @internal
+ *
+ * Am I missing something? Why we have the same thing in different names?
+ */
+void rb_define_class_variable(VALUE, const char*, VALUE);
+
+/**
+ * Resembles `Module#class_variables`. List up the variables defined at the
+ * receiver. This includes the names of constants in any included modules,
+ * unless `argv[0]` is ::RUBY_Qfalse.
+ *
+ * The implementation makes no guarantees about the order in which the
+ * constants are yielded.
+ *
+ * @param[in] argc Either 0 or 1.
+ * @param[in] argv Pointer to ::RUBY_Qfalse, if `argc == 1`.
+ * @param[in] recv Target class.
+ * @return An array of symbols, which are class variable names under
+ * `recv`.
+ */
+VALUE rb_mod_class_variables(int argc, const VALUE *argv, VALUE recv);
+
+/**
+ * Resembles `Module#remove_class_variable`.
+ *
+ * @param[out] mod Target class.
+ * @param[in] name Variable name to remove, either in Symbol or String.
+ * @return What was removed.
+ * @pre Instance variable named `name` is deleted from `obj`.
+ */
+VALUE rb_mod_remove_cvar(VALUE mod, VALUE name);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_VARIABLE_H */
diff --git a/include/ruby/internal/intern/vm.h b/include/ruby/internal/intern/vm.h
new file mode 100644
index 0000000000..f0b54c702c
--- /dev/null
+++ b/include/ruby/internal/intern/vm.h
@@ -0,0 +1,433 @@
+#ifndef RBIMPL_INTERN_VM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERN_VM_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Public APIs related to rb_cRubyVM.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* vm.c */
+
+/**
+ * Resembles `__LINE__`.
+ *
+ * @retval 0 Current execution context not in a ruby method.
+ * @retval otherwise The current line number of the current thread of the
+ * current ractor of the current execution context.
+ */
+int rb_sourceline(void);
+
+/**
+ * Resembles `__FILE__`.
+ *
+ * @retval 0 Current execution context not in a ruby method.
+ * @retval otherwise The current source path of the current thread of the
+ * current ractor of the current execution context.
+ * @note This may or may not be an absolute path.
+ */
+const char *rb_sourcefile(void);
+
+/**
+ * Resembles `__method__`.
+ *
+ * @param[out] idp Return buffer for method id.
+ * @param[out] klassp Return buffer for class.
+ * @retval 0 Current execution context not in a method.
+ * @retval 1 Successful return.
+ * @post Upon successful return `*idp` and `*klassp` are updated to have
+ * the current method name and its defined class respectively.
+ * @note Both parameters can be `NULL`.
+ */
+int rb_frame_method_id_and_class(ID *idp, VALUE *klassp);
+
+/* vm_eval.c */
+
+/**
+ * Identical to rb_funcallv(), except it returns ::RUBY_Qundef instead of
+ * raising ::rb_eNoMethodError.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @retval RUBY_Qundef `recv` doesn't respond to `mid`.
+ * @retval otherwise What the method evaluates to.
+ */
+VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv);
+
+/**
+ * Identical to rb_check_funcall(), except you can specify how to handle the
+ * last element of the given array. It can also be seen as a routine identical
+ * to rb_funcallv_kw(), except it returns ::RUBY_Qundef instead of raising
+ * ::rb_eNoMethodError.
+ *
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arbitrary number of method arguments.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @retval RUBY_Qundef `recv` doesn't respond to `mid`.
+ * @retval otherwise What the method evaluates to.
+ */
+VALUE rb_check_funcall_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat);
+
+/**
+ * This API is practically a variant of rb_proc_call_kw() now. Historically
+ * when there still was a concept called `$SAFE`, this was an API for that.
+ * But we no longer have that. This function basically ended its role. It
+ * just remains here because of no harm.
+ *
+ * @param[in] cmd A string, or something callable.
+ * @param[in] arg Argument passed to the call.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `arg`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `arg`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @return What the command evaluates to.
+ */
+RBIMPL_ATTR_DEPRECATED_INTERNAL(4.0)
+VALUE rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat);
+
+/**
+ * Identical to rb_funcallv(), except it takes Ruby's array instead of C's.
+ * @param[in,out] recv Receiver of the method.
+ * @param[in] mid Name of the method to call.
+ * @param[in] args An instance of ::RArray.
+ * @exception rb_eNoMethodError No such method.
+ * @exception rb_eException Any exceptions happen inside.
+ * @return What the method evaluates to.
+ * @pre `args` must be an ::RArray. Call `to_ary` beforehand when
+ * necessary.
+ */
+VALUE rb_apply(VALUE recv, ID mid, VALUE args);
+
+/**
+ * Evaluates a string containing Ruby source code, or the given block, within
+ * the context of the receiver. In order to set the context, the variable
+ * `self` is set to `recv` while the code is executing, giving the code access
+ * to `recv`'s instance variables and private methods.
+ *
+ * When given a block, `recv` is also passed in as the block's only argument.
+ *
+ * When given a string, the optional second and third parameters supply a
+ * filename and starting line number that are used when reporting compilation
+ * errors.
+ *
+ * @param[in] argc Number of objects in `argv`
+ * @param[in] argv C array of 0 up to 3 elements.
+ * @param[in] recv The object in question.
+ * @return What was evaluated.
+ */
+VALUE rb_obj_instance_eval(int argc, const VALUE *argv, VALUE recv);
+
+/**
+ * Executes the given block within the context of the receiver. In order to
+ * set the context, the variable `self` is set to `recv` while the code is
+ * executing, giving the code access to `recv`'s instance variables. Arguments
+ * are passed as block parameters.
+ *
+ * @param[in] argc Number of objects in `argv`
+ * @param[in] argv Arbitrary parameters to be passed to the block.
+ * @param[in] recv The object in question.
+ * @return What was evaluated.
+ * @note Don't confuse this with rb_obj_instance_eval(). The key
+ * difference is whether you can pass arbitrary parameters to the
+ * block, like this:
+ *
+ * ```ruby
+ * class Foo
+ * def initialize
+ * @foo = 5
+ * end
+ * end
+ * Foo.new.instance_exec(7) {|i| @foo + i } # => 12
+ * ```
+ */
+VALUE rb_obj_instance_exec(int argc, const VALUE *argv, VALUE recv);
+
+/**
+ * Identical to rb_obj_instance_eval(), except it evaluates within the context
+ * of module.
+ *
+ * @param[in] argc Number of objects in `argv`
+ * @param[in] argv C array of 0 up to 3 elements.
+ * @param[in] mod The module in question.
+ * @pre `mod` must be a Module.
+ * @return What was evaluated.
+ */
+VALUE rb_mod_module_eval(int argc, const VALUE *argv, VALUE mod);
+
+/**
+ * Identical to rb_obj_instance_exec(), except it evaluates within the context
+ * of module.
+ *
+ * @param[in] argc Number of objects in `argv`
+ * @param[in] argv Arbitrary parameters to be passed to the block.
+ * @param[in] mod The module in question.
+ * @pre `mod` must be a Module.
+ * @return What was evaluated.
+ */
+VALUE rb_mod_module_exec(int argc, const VALUE *argv, VALUE mod);
+
+/* vm_method.c */
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define HAVE_RB_DEFINE_ALLOC_FUNC 1
+
+/**
+ * This is the type of functions that ruby calls when trying to allocate an
+ * object. It is sometimes necessary to allocate extra memory regions for an
+ * object. When you define a class that uses ::RTypedData, it is typically the
+ * case. On such situations define a function of this type and pass it to
+ * rb_define_alloc_func().
+ *
+ * @param[in] klass The class that this function is registered.
+ * @return A newly allocated instance of `klass`.
+ */
+typedef VALUE (*rb_alloc_func_t)(VALUE klass);
+
+/**
+ * Sets the allocator function of a class.
+ *
+ * @param[out] klass The class to modify.
+ * @param[in] func An allocator function for the class.
+ * @pre `klass` must be an instance of Class.
+ */
+void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func);
+
+/**
+ * Deletes the allocator function of a class. It is sometimes desirable to
+ * restrict creation of an instance of a class. For example it rarely makes
+ * sense for a DB adaptor class to allow programmers creating DB row objects
+ * without querying the DB itself. You can kill sporadic creation of such
+ * objects then, by nullifying the allocator function using this API.
+ *
+ * @param[out] klass The class to modify.
+ * @pre `klass` must be an instance of Class.
+ */
+void rb_undef_alloc_func(VALUE klass);
+
+/**
+ * Queries the allocator function of a class.
+ *
+ * @param[in] klass The class in question.
+ * @pre `klass` must be an instance of Class.
+ * @retval 0 No allocator function is registered.
+ * @retval otherwise The allocator function.
+ *
+ * @internal
+ *
+ * Who cares? @shyouhei finds no practical usage of the return value. Maybe we
+ * need KonMari.
+ */
+rb_alloc_func_t rb_get_alloc_func(VALUE klass);
+
+/**
+ * Clears the inline constant caches associated with a particular ID. Extension
+ * libraries should not bother with such things. Just forget about this API (or
+ * even, the presence of constant caches).
+ */
+void rb_clear_constant_cache_for_id(ID id);
+
+/**
+ * Resembles `alias`.
+ *
+ * @param[out] klass Where to define an alias.
+ * @param[in] dst New name.
+ * @param[in] src Existing name.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @exception rb_eNameError No such method named `src`.
+ * @post `klass` has a method named `dst`, which is the identical to its
+ * method named `src`.
+ */
+void rb_alias(VALUE klass, ID dst, ID src);
+
+/**
+ * This function resembles now-deprecated `Module#attr`.
+ *
+ * @param[out] klass Where to define an attribute.
+ * @param[in] name Name of an instance variable.
+ * @param[in] need_reader Whether attr_reader is needed.
+ * @param[in] need_writer Whether attr_writer is needed.
+ * @param[in] honour_visibility Whether to use the current visibility.
+ * @exception rb_eTypeError `klass` is not a class.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @post If `need_reader` is set `klass` has a method named `name`.
+ * @post If `need_writer` is set `klass` has a method named `name=`.
+ *
+ * @internal
+ *
+ * The three `int` arguments should have been bool, but there was no such thing
+ * like a bool when K&R was used in this project.
+ */
+void rb_attr(VALUE klass, ID name, int need_reader, int need_writer, int honour_visibility);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Removes a method. Don't confuse this to rb_undef_method(), which doesn't
+ * remove a method. This one resembles `Module#remove_method`.
+ *
+ * @param[out] klass The class to remove a method.
+ * @param[in] name Name of a method to be removed.
+ * @exception rb_eTypeError `klass` is a non-module.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @exception rb_eNameError No such method.
+ * @see rb_undef_method
+ */
+void rb_remove_method(VALUE klass, const char *name);
+
+/**
+ * Identical to rb_remove_method(), except it accepts the method name as ::ID.
+ *
+ * @param[out] klass The class to remove a method.
+ * @param[in] mid Name of a method to be removed.
+ * @exception rb_eTypeError `klass` is a non-module.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @exception rb_eNameError No such method.
+ * @see rb_undef
+ */
+void rb_remove_method_id(VALUE klass, ID mid);
+
+/**
+ * Queries if the klass has this method. This function has only one line of
+ * document in the implementation that states "// deprecated". Don't know what
+ * that means though.
+ *
+ * @param[in] klass The class in question.
+ * @param[in] id The method name to query.
+ * @param[in] ex Undocumented magic value.
+ * @retval false Method not found.
+ * @retval true There is a method.
+ * @pre `klass` must be a module.
+ *
+ * @internal
+ *
+ * @shyouhei has no motivation to describe what should be passed to `ex`. It
+ * seems this function should just be trashed.
+ */
+int rb_method_boundp(VALUE klass, ID id, int ex);
+
+/**
+ * Well... Let us hesitate from describing what a "basic definition" is. This
+ * nuanced concept should have been kept private. Just please. Don't touch
+ * it. This function is a badly distributed random number generator. Right?
+ *
+ * @param[in] klass The class in question.
+ * @param[in] mid The method name in question.
+ * @retval 1 It is.
+ * @retval 0 It isn't.
+ */
+int rb_method_basic_definition_p(VALUE klass, ID mid);
+
+/**
+ * Identical to rb_respond_to(), except it additionally takes the visibility
+ * parameter. This does not make difference unless the object has
+ * `respond_to?` undefined, but has `respond_to_missing?` defined. That case
+ * the passed argument becomes the second argument of `respond_to_missing?`.
+ *
+ * @param[in] obj The object in question.
+ * @param[in] mid The method name in question.
+ * @param[in] private_p This is the second argument of `obj`'s
+ * `respond_to_missing?`.
+ * @retval 1 Yes it does.
+ * @retval 0 No it doesn't.
+ */
+int rb_obj_respond_to(VALUE obj, ID mid, int private_p);
+
+/**
+ * Queries if the object responds to the method. This involves calling the
+ * object's `respond_to?` method.
+ *
+ * @param[in] obj The object in question.
+ * @param[in] mid The method name in question.
+ * @retval 1 Yes it does.
+ * @retval 0 No it doesn't.
+ */
+int rb_respond_to(VALUE obj, ID mid);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Raises ::rb_eNotImpError. This function is used as an argument to
+ * rb_define_method() etc.
+ *
+ * ```CXX
+ * rb_define_method(rb_cFoo, "foo", rb_f_notimplement, -1);
+ * ```
+ *
+ * @param argc Unused parameter.
+ * @param argv Unused parameter.
+ * @param obj Unused parameter.
+ * @param marker Unused parameter.
+ * @exception rb_eNotImpError Always.
+ * @return Never returns.
+ *
+ * @internal
+ *
+ * See also the Q&A section of include/ruby/internal/anyargs.h.
+ */
+VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker);
+#if !defined(RUBY_EXPORT) && defined(_WIN32)
+RUBY_EXTERN VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE marker);
+#define rb_f_notimplement (*rb_f_notimplement_)
+#endif
+
+/* vm_backtrace.c */
+
+/**
+ * Prints the backtrace out to the standard error. This just confuses people
+ * for no reason. Evil souls must only use it.
+ *
+ * @internal
+ *
+ * Actually it is very useful when called from an interactive GDB session.
+ */
+void rb_backtrace(void);
+
+/**
+ * Creates the good old fashioned array-of-strings style backtrace info.
+ *
+ * @return An array which contains strings, which are the textual
+ * representations of the backtrace locations of the current thread of
+ * the current ractor of the current execution context.
+ * @note Ruby scripts can access more sophisticated
+ * `Thread::Backtrace::Location`. But it seems there is no way for C
+ * extensions to use that API.
+ */
+VALUE rb_make_backtrace(void);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERN_VM_H */
diff --git a/include/ruby/internal/interpreter.h b/include/ruby/internal/interpreter.h
new file mode 100644
index 0000000000..a10e7ad2d8
--- /dev/null
+++ b/include/ruby/internal/interpreter.h
@@ -0,0 +1,304 @@
+#ifndef RBIMPL_INTERPRETER_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_INTERPRETER_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Interpreter embedding APIs.
+ */
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * @defgroup embed CRuby Embedding APIs
+ *
+ * CRuby interpreter APIs. These are APIs to embed MRI interpreter into your
+ * program.
+ * These functions are not a part of Ruby extension library API.
+ * Extension libraries of Ruby should not depend on these functions.
+ *
+ * @{
+ */
+
+/**
+ * @defgroup ruby1 ruby(1) implementation
+ *
+ * A part of the implementation of ruby(1) command.
+ * Other programs that embed Ruby interpreter do not always need to use these
+ * functions.
+ *
+ * @{
+ */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Initializes the process for libruby.
+ *
+ * This function assumes this process is `ruby(1)` and it has just started.
+ * Usually programs that embed CRuby interpreter may not call this function,
+ * and may do their own initialization.
+ *
+ * @param[in] argc Pointer to process main's `argc`.
+ * @param[in] argv Pointer to process main's `argv`.
+ * @warning `argc` and `argv` cannot be `NULL`.
+ *
+ * @internal
+ *
+ * AFAIK Ruby does write to argv, especially `argv[0][0]`, via setproctitle(3).
+ * It is intentional that the argument is not const-qualified.
+ */
+void ruby_sysinit(int *argc, char ***argv);
+
+/**
+ * Calls ruby_setup() and check error.
+ *
+ * Prints errors and calls exit(3) if an error occurred.
+ */
+void ruby_init(void);
+
+/**
+ * Processes command line arguments and compiles the Ruby source to execute.
+ *
+ * This function does:
+ * - Processes the given command line flags and arguments for `ruby(1)`
+ * - Compiles the source code from the given argument, `-e` or `stdin`, and
+ * - Returns the compiled source as an opaque pointer to an internal data
+ * structure
+ *
+ * @param[in] argc Process main's `argc`.
+ * @param[in] argv Process main's `argv`.
+ * @return An opaque pointer to the compiled source or an internal special
+ * value. Pass it to ruby_executable_node() to detect which.
+ * @see ruby_executable_node
+ */
+void* ruby_options(int argc, char** argv);
+
+/**
+ * Checks the return value of ruby_options().
+ *
+ * ruby_options() sometimes returns a special value to indicate this process
+ * should immediately exit. This function checks if the case. Also stores the
+ * exit status that the caller have to pass to exit(3) into `*status`.
+ *
+ * @param[in] n A return value of ruby_options().
+ * @param[out] status Pointer to the exit status of this process.
+ * @retval 0 The given value is such a special value.
+ * @retval otherwise The given opaque pointer is actually a compiled
+ * source.
+ */
+int ruby_executable_node(void *n, int *status);
+
+/**
+ * Runs the given compiled source and exits this process.
+ *
+ * @param[in] n Opaque "node" pointer.
+ * @retval EXIT_SUCCESS Successfully run the source.
+ * @retval EXIT_FAILURE An error occurred.
+ */
+int ruby_run_node(void *n);
+
+/* version.c */
+/** Prints the version information of the CRuby interpreter to stdout. */
+void ruby_show_version(void);
+
+#ifndef ruby_show_copyright
+/** Prints the copyright notice of the CRuby interpreter to stdout. */
+void ruby_show_copyright(void);
+#endif
+
+/**
+ * A convenience macro to call ruby_init_stack().
+ * Must be placed just after variable declarations.
+ */
+#define RUBY_INIT_STACK \
+ VALUE variable_in_this_stack_frame; \
+ ruby_init_stack(&variable_in_this_stack_frame);
+/** @} */
+
+/**
+ * Set stack bottom of Ruby implementation.
+ *
+ * You must call this function before any heap allocation by Ruby
+ * implementation. Or GC will break living objects.
+ *
+ * @param[in] addr A pointer somewhere on the stack, near its bottom.
+ */
+void ruby_init_stack(void *addr);
+
+/**
+ * Initializes the VM and builtin libraries.
+ *
+ * @retval 0 Initialization succeeded.
+ * @retval otherwise An error occurred.
+ *
+ * @internal
+ *
+ * Though not a part of our public API, the return value is in fact an enum
+ * ruby_tag_type. You can see the potential "otherwise" values by looking at
+ * vm_core.h.
+ */
+int ruby_setup(void);
+
+/**
+ * Destructs the VM.
+ *
+ * Runs the VM finalization processes as well as ruby_finalize(), and frees
+ * resources used by the VM.
+ *
+ * @param[in] ex Default value to the return value.
+ * @retval EXIT_FAILURE An error occurred.
+ * @retval ex Successful cleanup.
+ * @note This function does not raise any exception.
+ */
+int ruby_cleanup(int ex);
+
+/**
+ * Runs the VM finalization processes.
+ *
+ * `END{}` and procs registered by `Kernel.#at_exit` are executed here. See the
+ * Ruby language spec for more details.
+ *
+ * @note This function is allowed to raise an exception if an error occurred.
+ */
+void ruby_finalize(void);
+
+RBIMPL_ATTR_NORETURN()
+/** Calls ruby_cleanup() and exits the process. */
+void ruby_stop(int);
+
+/**
+ * Checks for stack overflow.
+ *
+ * @retval true NG machine stack is about to overflow.
+ * @retval false OK there still is a room in the stack.
+ *
+ * @internal
+ *
+ * Does anybody use it? So far @shyouhei have never seen any actual use-case.
+ */
+int ruby_stack_check(void);
+
+/**
+ * Queries what Ruby thinks is the machine stack. Ruby manages a region of
+ * memory. It calls that area the "machine stack". By calling this function,
+ * in spite of its name, you can obtain both one end of the stack and its
+ * length at once. Which means you can know the entire region.
+ *
+ * @param[out] topnotch On return the pointer points to the upmost address of
+ * the macihne stack that Ruby knows.
+ * @return Length of the machine stack that Ruby knows.
+ *
+ * @internal
+ *
+ * Does anybody use it? @shyouhei is quite skeptical if this is useful outside
+ * of the VM. Maybe it was a wrong idea to expose this API to 3rd parties.
+ */
+size_t ruby_stack_length(VALUE **topnotch);
+
+/**
+ * Identical to ruby_run_node(), except it returns an opaque execution status.
+ * You can pass it to rb_cleanup().
+ *
+ * @param[in] n Opaque "node" pointer.
+ * @retval 0 Successful end-of-execution.
+ * @retval otherwise An error occurred.
+ *
+ * @internal
+ *
+ * Though not a part of our public API, the return value is in fact an enum
+ * ruby_tag_type. You can see the potential "otherwise" values by looking at
+ * vm_core.h.
+ */
+int ruby_exec_node(void *n);
+
+/**
+ * Sets the current script name to this value.
+ *
+ * This is similar to `$0 = name` in Ruby level but also affects
+ * `Method#location` and others.
+ *
+ * @param[in] name File name to set.
+ */
+void ruby_script(const char* name);
+
+/**
+ * Identical to ruby_script(), except it takes the name as a Ruby String
+ * instance.
+ *
+ * @param[in] name File name to set.
+ */
+void ruby_set_script_name(VALUE name);
+
+/** Defines built-in variables */
+void ruby_prog_init(void);
+
+/**
+ * Sets argv that ruby understands. Your program might have its own command
+ * line parameters etc. Handle them as you wish, and pass remaining parts of
+ * argv here.
+ *
+ * @param[in] argc Number of elements of `argv`.
+ * @param[in] argv Command line arguments.
+ */
+void ruby_set_argv(int argc, char **argv);
+
+/**
+ * Identical to ruby_options(), except it raises ruby-level exceptions on
+ * failure.
+ *
+ * @param[in] argc Process main's `argc`.
+ * @param[in] argv Process main's `argv`.
+ * @return An opaque "node" pointer.
+ */
+void *ruby_process_options(int argc, char **argv);
+
+/**
+ * Sets up `$LOAD_PATH`.
+ *
+ * @internal
+ *
+ * @shyouhei guesses this has to be called at very later stage, at least after
+ * the birth of object system. But is not exactly sure when.
+ */
+void ruby_init_loadpath(void);
+
+/**
+ * Appends the given path to the end of the load path.
+ *
+ * @pre ruby_init_loadpath() must be done beforehand.
+ * @param[in] path The path you want to push to the load path.
+ */
+void ruby_incpush(const char *path);
+
+/**
+ * Clear signal handlers.
+ *
+ * Ruby installs its own signal handler (apart from those which user scripts
+ * set). This is to clear that. Must be called when the ruby part terminates,
+ * before switching to your program's own logic.
+ */
+void ruby_sig_finalize(void);
+
+/** @} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_INTERPRETER_H */
diff --git a/include/ruby/internal/iterator.h b/include/ruby/internal/iterator.h
new file mode 100644
index 0000000000..891045363e
--- /dev/null
+++ b/include/ruby/internal/iterator.h
@@ -0,0 +1,472 @@
+#ifndef RBIMPL_ITERATOR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_ITERATOR_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Block related APIs.
+ */
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define RB_BLOCK_CALL_FUNC_STRICT 1
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define RUBY_BLOCK_CALL_FUNC_TAKES_BLOCKARG 1
+
+/**
+ * Shim for block function parameters. Historically ::rb_block_call_func_t had
+ * only two parameters. Over time it evolved to have much more than that. By
+ * using this macro you can absorb such API differences.
+ *
+ * ```CXX
+ * // This works since 2.1.0
+ * VALUE my_own_iterator(RB_BLOCK_CALL_FUNC_ARGLIST(y, c));
+ * ```
+ */
+#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg) \
+ VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg
+
+/**
+ * This is the type of a function that the interpreter expect for C-backended
+ * blocks. Blocks are often written in Ruby. But C extensions might want to
+ * have their own blocks. In order to do so authors have to create a separate
+ * C function of this type, and pass its pointer to rb_block_call().
+ *
+ * ```CXX
+ * VALUE
+ * my_own_iterator(RB_BLOCK_CALL_FUNC_ARGLIST(y, c))
+ * {
+ * const auto plus = rb_intern("+");
+ * return rb_funcall(c, plus, 1, y);
+ * }
+ *
+ * VALUE
+ * my_own_method(VALUE self)
+ * {
+ * const auto each = rb_intern("each");
+ * return rb_block_call(self, each, 0, 0, my_own_iterator, self);
+ * }
+ * ```
+ */
+typedef VALUE rb_block_call_func(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg));
+
+/**
+ * Shorthand type that represents an iterator-written-in-C function pointer.
+ */
+typedef rb_block_call_func *rb_block_call_func_t;
+
+/**
+ * This is a shorthand of calling `obj.each`.
+ *
+ * @param[in] obj The receiver.
+ * @return What `obj.each` returns.
+ *
+ * @internal
+ *
+ * Does anyone still need it? This API was to use with rb_iterate(), which is
+ * marked deprecated (see below). Old idiom to call an iterator was:
+ *
+ * ```CXX
+ * VALUE recv;
+ * VALUE iter_func(ANYARGS);
+ * VALUE iter_data;
+ * rb_iterate(rb_each, recv, iter_func, iter_data);
+ * ```
+ */
+VALUE rb_each(VALUE obj);
+
+/**
+ * Yields the block. In Ruby there is a concept called a block. You can pass
+ * one to a method. In a method, when called with a block, you can yield it
+ * using this function.
+ *
+ * ```CXX
+ * VALUE
+ * iterate(VALUE self)
+ * {
+ * extern int get_n(VALUE);
+ * extern VALUE get_v(VALUE, VALUE);
+ * const auto n = get_n(self);
+ *
+ * for (int i=0; i<n; i++) {
+ * auto v = get_v(self, i);
+ *
+ * rb_yield(v);
+ * }
+ * return self;
+ * }
+ * ```
+ *
+ * @param[in] val Passed to the block.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield(VALUE val);
+
+/**
+ * Identical to rb_yield(), except it takes variadic number of parameters and
+ * pass them to the block.
+ *
+ * @param[in] n Number of parameters.
+ * @param[in] ... List of arguments passed to the block.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield_values(int n, ...);
+
+/**
+ * Identical to rb_yield_values(), except it takes the parameters as a C array
+ * instead of variadic arguments.
+ *
+ * @param[in] n Number of parameters.
+ * @param[in] argv List of arguments passed to the block.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield_values2(int n, const VALUE *argv);
+
+/**
+ * Identical to rb_yield_values2(), except you can specify how to handle the
+ * last element of the given array.
+ *
+ * @param[in] n Number of parameters.
+ * @param[in] argv List of arguments passed to the block.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `ary`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `ary`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS makes no sense here.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield_values_kw(int n, const VALUE *argv, int kw_splat);
+
+/**
+ * Identical to rb_yield_values(), except it splats an array to generate the
+ * list of parameters.
+ *
+ * @param[in] ary Array to splat.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield_splat(VALUE ary);
+
+/**
+ * Identical to rb_yield_splat(), except you can specify how to handle the last
+ * element of the given array.
+ *
+ * @param[in] ary Array to splat.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `ary`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `ary`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS makes no sense here.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield_splat_kw(VALUE ary, int kw_splat);
+
+/**
+ * Pass a passed block.
+ *
+ * Sometimes you want to "pass" a block form one method to another. Suppose
+ * you have this Ruby method `foo`:
+ *
+ * ```ruby
+ * def foo(x, y)
+ * x.open(y) do |*z|
+ * yield(*z)
+ * end
+ * end
+ * ```
+ *
+ * And suppose you want to translate this into C. Then rb_yield_block()
+ * function is usable in this situation.
+ *
+ * ```CXX
+ * VALUE
+ * foo_translated_into_C(VALUE self, VALUE x, VALUE y)
+ * {
+ * const auto open = rb_intern("open");
+ *
+ * return rb_block_call(x, open, 1, &y, rb_yield_block, Qfalse);
+ * // ^^^^^^^^^^^^^^ Here.
+ * }
+ * ```
+ *
+ * @see rb_funcall_passing_block
+ *
+ * @internal
+ *
+ * @shyouhei honestly doesn't understand why this is needed, given there
+ * already was rb_funcall_passing_block() at the time it was implemented. If
+ * somebody knows its raison d'etre, please improve the document :FIXME:
+ */
+VALUE rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)); /* rb_block_call_func */
+
+/**
+ * Determines if the current method is given a keyword argument.
+ *
+ * @retval false No keyword argument is given.
+ * @retval true Keyword argument(s) are given.
+ * @ingroup defmethod
+ */
+int rb_keyword_given_p(void);
+
+/**
+ * Determines if the current method is given a block.
+ *
+ * @retval false No block is given.
+ * @retval true A block is given.
+ * @ingroup defmethod
+ *
+ * @internal
+ *
+ * This function should have returned a bool. But at the time it was designed
+ * the project was entirely written in K&R C.
+ */
+int rb_block_given_p(void);
+
+/**
+ * Declares that the current method needs a block.
+ *
+ * @exception rb_eLocalJumpError No block given.
+ * @ingroup defmethod
+ */
+void rb_need_block(void);
+
+/**
+ * Identical to rb_funcallv(), except it additionally passes a function as a
+ * block. When the method yields, `proc` is called with the yielded value as
+ * its first argument, and `data2` as the second. Yielded values would be
+ * packed into an array if multiple values are yielded at once.
+ *
+ * @param[in,out] obj Receiver.
+ * @param[in] mid Method signature.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arguments passed to `obj.mid`.
+ * @param[in] proc A function acts as a block.
+ * @param[in,out] data2 Passed to `proc` as the data2 parameter.
+ * @return What `obj.mid` returns.
+ */
+VALUE rb_block_call(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t proc, VALUE data2);
+
+/**
+ * Identical to rb_funcallv_kw(), except it additionally passes a function as a
+ * block. It can also be seen as a routine identical to rb_block_call(),
+ * except it handles keyword-ness of `argv[argc-1]`.
+ *
+ * @param[in,out] obj Receiver.
+ * @param[in] mid Method signature.
+ * @param[in] argc Number of arguments including the keywords.
+ * @param[in] argv Arguments passed to `obj.mid`.
+ * @param[in] proc A function acts as a block.
+ * @param[in,out] data2 Passed to `proc` as the data2 parameter.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS Pass keyword arguments if the current method
+ * was called with keyword arguments.
+ * @return What `obj.mid` returns.
+ */
+VALUE rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t proc, VALUE data2, int kw_splat);
+
+/**
+ * Identical to rb_rescue2(), except it does not take a list of exception
+ * classes. This is a shorthand of:
+ *
+ * ```CXX
+ * rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError, (VALUE)0);
+ * ```
+ *
+ * @param[in] b_proc A function which potentially raises an exception.
+ * @param[in,out] data1 Passed to `b_proc`.
+ * @param[in] r_proc A function which rescues an exception in `b_proc`.
+ * @param[in,out] data2 The first argument of `r_proc`.
+ * @return The return value of `b_proc` if no exception occurs, or the
+ * return value of `r_proc` otherwise.
+ * @see rb_rescue
+ * @see rb_ensure
+ * @see rb_protect
+ * @ingroup exception
+ */
+VALUE rb_rescue(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*r_proc)(VALUE, VALUE), VALUE data2);
+
+/**
+ * An equivalent of `rescue` clause.
+ *
+ * First it calls the function `b_proc` with `data1` as the argument. If
+ * nothing is thrown the function happily returns the return value of `b_proc`.
+ * When `b_proc` raises an exception, and the exception is a kind of one of the
+ * given exception classes, it then calls `r_proc` with `data2` and that
+ * exception. If the exception does not match any of them, it propagates.
+ *
+ * @param[in] b_proc A function which potentially raises an exception.
+ * @param[in,out] data1 Passed to `b_proc`.
+ * @param[in] r_proc A function which rescues an exception in `b_proc`.
+ * @param[in,out] data2 The first argument of `r_proc`.
+ * @param[in] ... 1 or more exception classes. Must be terminated by
+ * `(VALUE)0`
+ * @return The return value of `b_proc` if no exception occurs, or the
+ * return value of `r_proc` otherwise.
+ * @see rb_rescue
+ * @see rb_ensure
+ * @see rb_protect
+ * @ingroup exception
+ */
+VALUE rb_rescue2(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*r_proc)(VALUE, VALUE), VALUE data2, ...);
+
+/**
+ * Identical to rb_rescue2(), except it takes `va_list` instead of variadic
+ * number of arguments. This is exposed to 3rd parties because inline
+ * functions use it. Basically you don't have to bother.
+ *
+ * @param[in] b_proc A function which potentially raises an exception.
+ * @param[in,out] data1 Passed to `b_proc`.
+ * @param[in] r_proc A function which rescues an exception in `b_proc`.
+ * @param[in,out] data2 The first argument of `r_proc`.
+ * @param[in] ap 1 or more exception classes. Must be terminated by
+ * `(VALUE)0`
+ * @return The return value of `b_proc` if no exception occurs, or the
+ * return value of `r_proc` otherwise.
+ * @see rb_rescue
+ * @see rb_ensure
+ * @see rb_protect
+ * @ingroup exception
+ */
+VALUE rb_vrescue2(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*r_proc)(VALUE, VALUE), VALUE data2, va_list ap);
+
+/**
+ * An equivalent to `ensure` clause. Calls the function `b_proc` with `data1`
+ * as the argument, then calls `e_proc` with `data2` when execution terminated.
+ *
+ * @param[in] b_proc A function representing begin clause.
+ * @param[in,out] data1 Passed to `b_proc`.
+ * @param[in] e_proc A function representing ensure clause.
+ * @param[in,out] data2 Passed to `e_proc`.
+ * @retval RUBY_Qnil exception occurred inside of `b_proc`.
+ * @retval otherwise The return value of `b_proc`.
+ * @see rb_rescue
+ * @see rb_rescue2
+ * @see rb_protect
+ * @ingroup exception
+ */
+VALUE rb_ensure(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE data2);
+
+/**
+ * Executes the passed block and catches values thrown from inside of it.
+ *
+ * In case the block does not contain any throw`, this function returns the
+ * value of the last expression evaluated.
+ *
+ * ```CXX
+ * VALUE
+ * iter(RB_BLOCK_CALL_FUNC_ARGLIST(yielded, callback))
+ * {
+ * return INT2FIX(123);
+ * }
+ *
+ * VALUE
+ * method(VALUE self)
+ * {
+ * return rb_catch("tag", iter, Qnil); // returns 123
+ * }
+ * ```
+ *
+ * In case there do exist `throw`, Ruby searches up its execution context for a
+ * `catch` block. When a matching catch is found, the block stops executing
+ * and returns that thrown value instead.
+ *
+ * ```CXX
+ * VALUE
+ * iter(RB_BLOCK_CALL_FUNC_ARGLIST(yielded, callback))
+ * {
+ * rb_throw("tag", 456);
+ * return INT2FIX(123);
+ * }
+ *
+ * VALUE
+ * method(VALUE self)
+ * {
+ * return rb_catch("tag", iter, Qnil); // returns 456
+ * }
+ * ```
+ *
+ * @param[in] tag Arbitrary tag string.
+ * @param[in] func Function pointer that acts as a block.
+ * @param[in,out] data Extra parameter passed to `func`.
+ * @return Either caught value for `tag`, or the return value of `func`
+ * if nothing is thrown.
+ */
+VALUE rb_catch(const char *tag, rb_block_call_func_t func, VALUE data);
+
+/**
+ * Identical to rb_catch(), except it catches arbitrary Ruby objects.
+ *
+ * @param[in] tag Arbitrary tag object.
+ * @param[in] func Function pointer that acts as a block.
+ * @param[in,out] data Extra parameter passed to `func`.
+ * @return Either caught value for `tag`, or the return value of `func`
+ * if nothing is thrown.
+ */
+VALUE rb_catch_obj(VALUE tag, rb_block_call_func_t func, VALUE data);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Transfers control to the end of the active `catch` block waiting for `tag`.
+ * Raises rb_eUncughtThrow if there is no `catch` block for the tag. The
+ * second parameter supplies a return value for the `catch` block, which
+ * otherwise defaults to ::RUBY_Qnil. For examples, see rb_catch().
+ *
+ * @param[in] tag Tag string.
+ * @param[in] val Value to throw.
+ * @exception rb_eUncughtThrow There is no corresponding `catch` clause.
+ * @note It never returns.
+ */
+void rb_throw(const char *tag, VALUE val);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Identical to rb_throw(), except it allows arbitrary Ruby object to become a
+ * tag.
+ *
+ * @param[in] tag Arbitrary object.
+ * @param[in] val Value to throw.
+ * @exception rb_eUncughtThrow There is no corresponding `catch` clause.
+ * @note It never returns.
+ */
+void rb_throw_obj(VALUE tag, VALUE val);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_ITERATOR_H */
diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h
new file mode 100644
index 0000000000..cd099f85db
--- /dev/null
+++ b/include/ruby/internal/memory.h
@@ -0,0 +1,767 @@
+#ifndef RBIMPL_MEMORY_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_MEMORY_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Memory management stuff.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+
+#if defined(_MSC_VER) && defined(_WIN64)
+# include <intrin.h>
+# if defined(_M_AMD64)
+# pragma intrinsic(_umul128)
+# endif
+# if defined(_M_ARM64)
+# pragma intrinsic(__umulh)
+# endif
+#endif
+
+#include "ruby/internal/attr/alloc_size.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/attr/restrict.h"
+#include "ruby/internal/attr/returns_nonnull.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/has/builtin.h"
+#include "ruby/internal/stdalign.h"
+#include "ruby/internal/stdbool.h"
+#include "ruby/internal/stdckdint.h"
+#include "ruby/internal/xmalloc.h"
+#include "ruby/backward/2/limits.h"
+#include "ruby/backward/2/long_long.h"
+#include "ruby/backward/2/assume.h"
+#include "ruby/defines.h"
+
+/** @cond INTERNAL_MACRO */
+
+/* Make alloca work the best possible way. */
+#if defined(alloca)
+# /* Take that. */
+#elif RBIMPL_HAS_BUILTIN(__builtin_alloca)
+# define alloca __builtin_alloca
+#elif defined(_AIX)
+# pragma alloca
+#elif defined(__cplusplus)
+extern "C" void *alloca(size_t);
+#else
+extern void *alloca();
+#endif
+
+/** @endcond */
+
+#if defined(__DOXYGEN__)
+/**
+ * @private
+ *
+ * Type that is as twice wider as size_t. This is an implementation detail of
+ * rb_mul_size_overflow(). People should not use it. This is not a good name
+ * either.
+ */
+typedef uint128_t DSIZE_T;
+#elif defined(HAVE_INT128_T) && SIZEOF_SIZE_T <= 8
+# define DSIZE_T uint128_t
+#elif SIZEOF_SIZE_T * 2 <= SIZEOF_LONG_LONG
+# define DSIZE_T unsigned LONG_LONG
+#endif
+
+/**
+ * @private
+ *
+ * Maximum possible number of bytes that #RB_ALLOCV can allocate using
+ * `alloca`. Anything beyond this is allocated using rb_alloc_tmp_buffer().
+ * This selection is transparent to users. People don't have to bother.
+ */
+#ifdef C_ALLOCA
+# define RUBY_ALLOCV_LIMIT 0
+#else
+# define RUBY_ALLOCV_LIMIT 1024
+#endif
+
+/**
+ * Prevents premature destruction of local objects. Ruby's garbage collector
+ * is conservative; it scans the C level machine stack as well. Possible in-
+ * use Ruby objects must remain visible on stack, to be properly marked as
+ * such. However contemporary C compilers do not interface well with this.
+ * Consider the following example:
+ *
+ * ```CXX
+ * auto s = rb_str_new_cstr(" world");
+ * auto sptr = RSTRING_PTR(s);
+ * auto t = rb_str_new_cstr("hello,"); // Possible GC invocation
+ * auto u = rb_str_cat_cstr(t, sptr);
+ *
+ * RB_GC_GUARD(s); // ensure `s` (and thus `sptr`) do not get GC-ed
+ * ```
+ *
+ * Here, without the #RB_GC_GUARD, the last use of `s` is _before_ the last use
+ * of `sptr`. Compilers could thus think `s` and `t` are allowed to overlap.
+ * That would eliminate `s` from the stack, while `sptr` is still in use. If
+ * our GC ran at that very moment, `s` gets swept out, which also destroys
+ * `sptr`. Boom! You got a SEGV.
+ *
+ * In order to prevent this scenario #RB_GC_GUARD must be placed _after_ the
+ * last use of `sptr`. Placing #RB_GC_GUARD before dereferencing `sptr` would
+ * be of no use.
+ *
+ * #RB_GC_GUARD would not be necessary at all in the above example if non-
+ * inlined function calls are made on the `s` variable after `sptr` is
+ * dereferenced. Thus, in the above example, calling any un-inlined function
+ * on `s` such as `rb_str_modify(s);` will ensure `s` stays on the stack or
+ * register to prevent a GC invocation from prematurely freeing it.
+ *
+ * Using the #RB_GC_GUARD macro is preferable to using the `volatile` keyword
+ * in C. #RB_GC_GUARD has the following advantages:
+ *
+ * - the intent of the macro use is clear.
+ *
+ * - #RB_GC_GUARD only affects its call site. OTOH `volatile` generates some
+ * extra code every time the variable is used, hurting optimisation.
+ *
+ * - `volatile` implementations may be buggy/inconsistent in some compilers
+ * and architectures. #RB_GC_GUARD is customisable for broken
+ * systems/compilers without negatively affecting other systems.
+ *
+ * - C++ since C++20 deprecates `volatile`. If you write your extension
+ * library in that language there is no escape but to use this macro.
+ *
+ * @param v A variable of ::VALUE type.
+ * @post `v` is still alive.
+ */
+#ifdef __GNUC__
+#define RB_GC_GUARD(v) \
+ (*__extension__ ({ \
+ volatile VALUE *rb_gc_guarded_ptr = &(v); \
+ __asm__("" : : "m"(rb_gc_guarded_ptr)); \
+ rb_gc_guarded_ptr; \
+ }))
+#elif defined _MSC_VER
+#define RB_GC_GUARD(v) (*rb_gc_guarded_ptr(&(v)))
+#else
+#define HAVE_RB_GC_GUARDED_PTR_VAL 1
+#define RB_GC_GUARD(v) (*rb_gc_guarded_ptr_val(&(v),(v)))
+#endif
+
+/* Casts needed because void* is NOT compatible with others in C++. */
+
+/**
+ * Convenient macro that allocates an array of n elements.
+ *
+ * @param type Type of array elements.
+ * @param n Length of the array.
+ * @exception rb_eNoMemError No space left for allocation.
+ * @exception rb_eArgError Integer overflow trying to calculate the length
+ * of continuous memory region of `n` elements of
+ * `type`.
+ * @return Storage instance that is capable of storing at least `n`
+ * elements of type `type`.
+ * @note It doesn't return NULL, even when `n` is zero.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+#define RB_ALLOC_N(type,n) RBIMPL_CAST((type *)ruby_xmalloc2((n), sizeof(type)))
+
+/**
+ * Shorthand of #RB_ALLOC_N with `n=1`.
+ *
+ * @param type Type of allocation.
+ * @exception rb_eNoMemError No space left for allocation.
+ * @return Storage instance that can hold an `type` object.
+ * @note It doesn't return NULL.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+#define RB_ALLOC(type) RBIMPL_CAST((type *)ruby_xmalloc(sizeof(type)))
+
+/**
+ * Identical to #RB_ALLOC_N() but also nullifies the allocated region before
+ * returning.
+ *
+ * @param type Type of array elements.
+ * @param n Length of the array.
+ * @exception rb_eNoMemError No space left for allocation.
+ * @exception rb_eArgError Integer overflow trying to calculate the length
+ * of continuous memory region of `n` elements of
+ * `type`.
+ * @return Storage instance that is capable of storing at least `n`
+ * elements of type `type`.
+ * @post Returned array is filled with zeros.
+ * @note It doesn't return NULL, even when `n` is zero.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+#define RB_ZALLOC_N(type,n) RBIMPL_CAST((type *)ruby_xcalloc((n), sizeof(type)))
+
+/**
+ * Shorthand of #RB_ZALLOC_N with `n=1`.
+ *
+ * @param type Type of allocation.
+ * @exception rb_eNoMemError No space left for allocation.
+ * @return Storage instance that can hold an `type` object.
+ * @post Returned object is filled with zeros.
+ * @note It doesn't return NULL.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+#define RB_ZALLOC(type) (RB_ZALLOC_N(type, 1))
+
+/**
+ * Convenient macro that reallocates an array with a new size.
+ *
+ * @param var A variable of `type`, which points to a storage
+ * instance that was previously returned from
+ * either
+ * - ruby_xmalloc(),
+ * - ruby_xmalloc2(),
+ * - ruby_xcalloc(),
+ * - ruby_xrealloc(), or
+ * - ruby_xrealloc2().
+ * @param type Type of allocation.
+ * @param n Requested new size of each element.
+ * @exception rb_eNoMemError No space left for allocation.
+ * @exception rb_eArgError Integer overflow trying to calculate the length
+ * of continuous memory region of `n` elements of
+ * `type`.
+ * @return Storage instance that is capable of storing at least `n`
+ * elements of type `type`.
+ * @pre The passed variable must point to a valid live storage instance.
+ * It is a failure to pass a variable that holds an already-freed
+ * pointer.
+ * @note It doesn't return NULL, even when `n` is zero.
+ * @warning Do not assume anything on the alignment of the return value.
+ * There is no guarantee that it inherits the passed argument's
+ * one.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+#define RB_REALLOC_N(var,type,n) \
+ ((var) = RBIMPL_CAST((type *)ruby_xrealloc2((void *)(var), (n), sizeof(type))))
+
+/**
+ * @deprecated This macro is dangerous (does not bother stack overflow at
+ * all). #RB_ALLOCV is the modern way to do the same thing.
+ * @param type Type of array elements.
+ * @param n Length of the array.
+ * @return A pointer on stack.
+ */
+#define ALLOCA_N(type,n) \
+ RBIMPL_CAST((type *)alloca(rbimpl_size_mul_or_raise(sizeof(type), (n))))
+
+/**
+ * Identical to #RB_ALLOCV_N(), except that it allocates a number of bytes and
+ * returns a void* .
+ *
+ * @param v A variable to hold the just-in-case opaque Ruby object.
+ * @param n Size of allocation, in bytes.
+ * @return A void pointer to `n` bytes storage.
+ * @note `n` may be evaluated twice.
+ */
+#define RB_ALLOCV(v, n) \
+ ((n) < RUBY_ALLOCV_LIMIT ? \
+ ((v) = 0, alloca(n)) : \
+ rb_alloc_tmp_buffer(&(v), (n)))
+
+/**
+ * Allocates a memory region, possibly on stack. If the given size exceeds
+ * #RUBY_ALLOCV_LIMIT, it allocates a dedicated opaque ruby object instead and
+ * let our GC sweep that region after use. Either way you can fire-and-forget.
+ *
+ * ```CXX
+ * #include <sys/types.h>
+ *
+ * VALUE
+ * foo(int n)
+ * {
+ * VALUE v;
+ * auto ptr = RB_ALLOCV(struct tms, v, n);
+ * ...
+ * // no need to free `ptr`.
+ * }
+ * ```
+ *
+ * If you want to be super-duper polite you can also explicitly state the end
+ * of use of such memory region by calling #RB_ALLOCV_END().
+ *
+ * @param type The type of array elements.
+ * @param v A variable to hold the just-in-case opaque Ruby object.
+ * @param n Number of elements requested to allocate.
+ * @return An array of `n` elements of `type`.
+ * @note `n` may be evaluated twice.
+ */
+#define RB_ALLOCV_N(type, v, n) \
+ RBIMPL_CAST((type *) \
+ (((size_t)(n) < RUBY_ALLOCV_LIMIT / sizeof(type)) ? \
+ ((v) = 0, alloca((n) * sizeof(type))) : \
+ rb_alloc_tmp_buffer2(&(v), (n), sizeof(type))))
+
+/**
+ * Polite way to declare that the given array is not used any longer. Calling
+ * this not mandatory. Our GC can baby-sit you. However it is not a very bad
+ * idea to use it when possible. Doing so could reduce memory footprint.
+ *
+ * @param v A variable previously passed to either #RB_ALLOCV/#RB_ALLOCV_N.
+ */
+#define RB_ALLOCV_END(v) rb_free_tmp_buffer(&(v))
+
+/**
+ * Handy macro to erase a region of memory.
+ *
+ * @param p Target pointer.
+ * @param type Type of `p[0]`
+ * @param n Length of `p`.
+ * @return `p`.
+ * @post First `n` elements of `p` are squashed.
+ */
+#define MEMZERO(p,type,n) memset((p), 0, rbimpl_size_mul_or_raise(sizeof(type), (n)))
+
+/**
+ * Handy macro to call memcpy.
+ *
+ * @param p1 Destination pointer.
+ * @param p2 Source pointer.
+ * @param type Type of `p2[0]`
+ * @param n Length of `p2`.
+ * @return `p1`.
+ * @post First `n` elements of `p2` are copied into `p1`.
+ */
+#define MEMCPY(p1,p2,type,n) ruby_nonempty_memcpy((p1), (p2), rbimpl_size_mul_or_raise(sizeof(type), (n)))
+
+/**
+ * Handy macro to call memmove.
+ *
+ * @param p1 Destination pointer.
+ * @param p2 Source pointer.
+ * @param type Type of `p2[0]`
+ * @param n Length of `p2`.
+ * @return `p1`.
+ * @post First `n` elements of `p2` are copied into `p1`.
+ */
+#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), rbimpl_size_mul_or_raise(sizeof(type), (n)))
+
+/**
+ * Handy macro to call memcmp
+ *
+ * @param p1 Target LHS.
+ * @param p2 Target RHS.
+ * @param type Type of `p1[0]`
+ * @param n Length of `p1`.
+ * @retval <0 `p1` is "less" than `p2`.
+ * @retval 0 `p1` is equal to `p2`.
+ * @retval >0 `p1` is "greater" than `p2`.
+ */
+#define MEMCMP(p1,p2,type,n) memcmp((p1), (p2), rbimpl_size_mul_or_raise(sizeof(type), (n)))
+
+#define ALLOC_N RB_ALLOC_N /**< @old{RB_ALLOC_N} */
+#define ALLOC RB_ALLOC /**< @old{RB_ALLOC} */
+#define ZALLOC_N RB_ZALLOC_N /**< @old{RB_ZALLOC_N} */
+#define ZALLOC RB_ZALLOC /**< @old{RB_ZALLOC} */
+#define REALLOC_N RB_REALLOC_N /**< @old{RB_REALLOC_N} */
+#define ALLOCV RB_ALLOCV /**< @old{RB_ALLOCV} */
+#define ALLOCV_N RB_ALLOCV_N /**< @old{RB_ALLOCV_N} */
+#define ALLOCV_END RB_ALLOCV_END /**< @old{RB_ALLOCV_END} */
+
+/**
+ * @private
+ *
+ * This is an implementation detail of rbimpl_size_mul_overflow() and
+ * rbimpl_size_add_overflow().
+ *
+ * @internal
+ *
+ * Expecting this struct to be eliminated by function inlinings. This is
+ * nothing more than std::variant<std::size_t> if we could use recent C++, but
+ * reality is we cannot.
+ */
+struct rbimpl_size_overflow_tag {
+ bool overflowed; /**< Whether overflow happened or not. */
+ size_t result; /**< Calculation result. */
+};
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_RESTRICT()
+RBIMPL_ATTR_RETURNS_NONNULL()
+RBIMPL_ATTR_ALLOC_SIZE((2))
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_ALLOCV(). People don't use this
+ * directly.
+ *
+ * @param[out] store Pointer to a variable.
+ * @param[in] len Requested number of bytes to allocate.
+ * @return Allocated `len` bytes array.
+ * @post `store` holds the corresponding tmp buffer object.
+ */
+void *rb_alloc_tmp_buffer(volatile VALUE *store, long len);
+
+RBIMPL_ATTR_RESTRICT()
+RBIMPL_ATTR_RETURNS_NONNULL()
+RBIMPL_ATTR_ALLOC_SIZE((2,3))
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_ALLOCV_N(). People don't use this
+ * directly.
+ *
+ * @param[out] store Pointer to a variable.
+ * @param[in] len Requested number of bytes to allocate.
+ * @param[in] count Number of elements in an array.
+ * @return Allocated `len` bytes array.
+ * @post `store` holds the corresponding tmp buffer object.
+ *
+ * @internal
+ *
+ * Although the meaning of `count` variable is clear, @shyouhei doesn't
+ * understand its needs.
+ */
+void *rb_alloc_tmp_buffer_with_count(volatile VALUE *store, size_t len,size_t count);
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_ALLOCV_END(). People don't use this
+ * directly.
+ *
+ * @param[out] store Pointer to a variable.
+ * @pre `store` is a NULL, or a pointer to a tmp buffer object.
+ * @post `*store` is ::RUBY_Qfalse.
+ * @post The object formerly stored in `store` is destroyed.
+ */
+void rb_free_tmp_buffer(volatile VALUE *store);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_ALLOCV_N(). People don't use this
+ * directly.
+ *
+ * @param[in] x Arbitrary value.
+ * @param[in] y Arbitrary value.
+ * @exception rb_eArgError `x` * `y` would integer overflow.
+ */
+void ruby_malloc_size_overflow(size_t x, size_t y);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * @private
+ *
+ * This is an implementation detail. People don't use this directly.
+ *
+ * @param[in] x Arbitrary value.
+ * @param[in] y Arbitrary value.
+ * @exception rb_eArgError `x` + `y` would integer overflow.
+ */
+void ruby_malloc_add_size_overflow(size_t x, size_t y);
+
+#ifdef HAVE_RB_GC_GUARDED_PTR_VAL
+volatile VALUE *rb_gc_guarded_ptr_val(volatile VALUE *ptr, VALUE val);
+#endif
+RBIMPL_SYMBOL_EXPORT_END()
+
+#ifdef _MSC_VER
+# pragma optimize("", off)
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_GC_GUARD(). People don't use this
+ * directly.
+ *
+ * @param[in] ptr A pointer to an on-stack C variable.
+ * @return `ptr` as-is.
+ */
+static inline volatile VALUE *
+rb_gc_guarded_ptr(volatile VALUE *ptr)
+{
+ return ptr;
+}
+
+# pragma optimize("", on)
+#endif
+
+/**
+ * @deprecated This function was an implementation detail of old
+ * #RB_ALLOCV_N(). We no longer use it. @shyouhei suspects that
+ * there are no actual usage now. However it was not marked as
+ * private before. We cannot delete it any longer.
+ * @param[in] a Arbitrary value.
+ * @param[in] b Arbitrary value.
+ * @param[in] max Possible maximum value.
+ * @param[out] c A pointer to return the computation result.
+ * @retval 1 `c` is insane.
+ * @retval 0 `c` is sane.
+ * @post `c` holds `a` * `b`, but could be overflowed.
+ */
+static inline int
+rb_mul_size_overflow(size_t a, size_t b, size_t max, size_t *c)
+{
+#ifdef DSIZE_T
+ RB_GNUC_EXTENSION DSIZE_T da, db, c2;
+ da = a;
+ db = b;
+ c2 = da * db;
+ if (c2 > max) return 1;
+ *c = RBIMPL_CAST((size_t)c2);
+#else
+ if (b != 0 && a > max / b) return 1;
+ *c = a * b;
+#endif
+ return 0;
+}
+
+#if defined(__DOXYGEN__)
+RBIMPL_ATTR_CONSTEXPR(CXX14)
+#elif RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0)
+RBIMPL_ATTR_CONSTEXPR(CXX14) /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70507 */
+#elif RBIMPL_COMPILER_SINCE(Clang, 7, 0, 0)
+RBIMPL_ATTR_CONSTEXPR(CXX14) /* https://bugs.llvm.org/show_bug.cgi?id=37633 */
+#endif
+RBIMPL_ATTR_CONST()
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_ALLOCV_N(). People don't use this
+ * directly.
+ *
+ * @param[in] x Arbitrary value.
+ * @param[in] y Arbitrary value.
+ * @return `{ overflowed, result }`, where `overflowed` is whether there is
+ * an integer overflow or not, and `result` is a (possibly
+ * overflowed) result of `x` * `y`.
+ *
+ * @internal
+ *
+ * This is in fact also an implementation detail of ruby_xmalloc2() etc.
+ */
+static inline struct rbimpl_size_overflow_tag
+rbimpl_size_mul_overflow(size_t x, size_t y)
+{
+ struct rbimpl_size_overflow_tag ret = { false, 0, };
+
+#if defined(ckd_mul)
+ ret.overflowed = ckd_mul(&ret.result, x, y);
+
+#elif RBIMPL_HAS_BUILTIN(__builtin_mul_overflow)
+ ret.overflowed = __builtin_mul_overflow(x, y, &ret.result);
+
+#elif defined(DSIZE_T)
+ RB_GNUC_EXTENSION DSIZE_T dx = x;
+ RB_GNUC_EXTENSION DSIZE_T dy = y;
+ RB_GNUC_EXTENSION DSIZE_T dz = dx * dy;
+ ret.overflowed = dz > SIZE_MAX;
+ ret.result = RBIMPL_CAST((size_t)dz);
+
+#elif defined(_MSC_VER) && defined(_M_AMD64)
+ unsigned __int64 dp = 0;
+ unsigned __int64 dz = _umul128(x, y, &dp);
+ ret.overflowed = RBIMPL_CAST((bool)dp);
+ ret.result = RBIMPL_CAST((size_t)dz);
+
+#elif defined(_MSC_VER) && defined(_M_ARM64)
+ ret.overflowed = __umulh(x, y) != 0;
+ ret.result = x * y;
+
+#else
+ /* https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap */
+ ret.overflowed = (y != 0) && (x > SIZE_MAX / y);
+ ret.result = x * y;
+#endif
+
+ return ret;
+}
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_ALLOCV_N(). People don't use this
+ * directly.
+ *
+ * @param[in] x Arbitrary value.
+ * @param[in] y Arbitrary value.
+ * @exception rb_eArgError Multiplication could integer overflow.
+ * @return `x` * `y`.
+ *
+ * @internal
+ *
+ * This is in fact also an implementation detail of ruby_xmalloc2() etc.
+ */
+static inline size_t
+rbimpl_size_mul_or_raise(size_t x, size_t y)
+{
+ struct rbimpl_size_overflow_tag size =
+ rbimpl_size_mul_overflow(x, y);
+
+ if (RB_LIKELY(! size.overflowed)) {
+ return size.result;
+ }
+ else {
+ ruby_malloc_size_overflow(x, y);
+ RBIMPL_UNREACHABLE_RETURN(0);
+ }
+}
+
+#if defined(__DOXYGEN__)
+RBIMPL_ATTR_CONSTEXPR(CXX14)
+#elif RBIMPL_COMPILER_SINCE(GCC, 7, 0, 0)
+RBIMPL_ATTR_CONSTEXPR(CXX14) /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70507 */
+#elif RBIMPL_COMPILER_SINCE(Clang, 7, 0, 0)
+RBIMPL_ATTR_CONSTEXPR(CXX14) /* https://bugs.llvm.org/show_bug.cgi?id=37633 */
+#endif
+RBIMPL_ATTR_CONST()
+/**
+ * @private
+ *
+ * This is an implementation detail. People don't use this directly.
+ *
+ * @param[in] x Arbitrary value.
+ * @param[in] y Arbitrary value.
+ * @return `{ overflowed, result }`, where `overflowed` is whether there is
+ * an integer overflow or not, and `result` is a (possibly
+ * overflowed) result of `x` + `y`.
+ *
+ * @internal
+ */
+static inline struct rbimpl_size_overflow_tag
+rbimpl_size_add_overflow(size_t x, size_t y)
+{
+ struct rbimpl_size_overflow_tag ret = { false, 0, };
+
+#if defined(ckd_add)
+ ret.overflowed = ckd_add(&ret.result, x, y);
+
+#elif RBIMPL_HAS_BUILTIN(__builtin_add_overflow)
+ ret.overflowed = __builtin_add_overflow(x, y, &ret.result);
+
+#elif defined(DSIZE_T)
+ RB_GNUC_EXTENSION DSIZE_T dx = x;
+ RB_GNUC_EXTENSION DSIZE_T dy = y;
+ RB_GNUC_EXTENSION DSIZE_T dz = dx + dy;
+ ret.overflowed = dz > SIZE_MAX;
+ ret.result = (size_t)dz;
+
+#else
+ ret.result = x + y;
+ ret.overflowed = ret.result < y;
+
+#endif
+
+ return ret;
+}
+
+/**
+ * @private
+ *
+ * This is an implementation detail. People don't use this directly.
+ *
+ * @param[in] x Arbitrary value.
+ * @param[in] y Arbitrary value.
+ * @exception rb_eArgError Multiplication could integer overflow.
+ * @return `x` + `y`.
+ *
+ * @internal
+ */
+static inline size_t
+rbimpl_size_add_or_raise(size_t x, size_t y)
+{
+ struct rbimpl_size_overflow_tag size =
+ rbimpl_size_add_overflow(x, y);
+
+ if (RB_LIKELY(!size.overflowed)) {
+ return size.result;
+ }
+ else {
+ ruby_malloc_add_size_overflow(x, y);
+ RBIMPL_UNREACHABLE_RETURN(0);
+ }
+}
+
+/**
+ * This is an implementation detail of #RB_ALLOCV_N(). People don't use this
+ * directly.
+ *
+ * @param[out] store Pointer to a variable.
+ * @param[in] count Number of elements in an array.
+ * @param[in] elsize Size of each elements.
+ * @return Region of `count` * `elsize` bytes.
+ * @post `store` holds the corresponding tmp buffer object.
+ *
+ * @internal
+ *
+ * We might want to deprecate this function and make a `rbimpl_` counterpart.
+ */
+static inline void *
+rb_alloc_tmp_buffer2(volatile VALUE *store, long count, size_t elsize)
+{
+ const size_t total_size = rbimpl_size_mul_or_raise(RBIMPL_CAST((size_t)count), elsize);
+ const size_t cnt = (total_size + sizeof(VALUE) - 1) / sizeof(VALUE);
+ return rb_alloc_tmp_buffer_with_count(store, total_size, cnt);
+}
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL((1))
+RBIMPL_ATTR_RETURNS_NONNULL()
+/* At least since 2004, glibc's <string.h> annotates memcpy to be
+ * __attribute__((__nonnull__(1, 2))). However it is safe to pass NULL to the
+ * source pointer, if n is 0. Let's wrap memcpy. */
+static inline void *
+ruby_nonempty_memcpy(void *dest, const void *src, size_t n)
+{
+ if (n) {
+ return memcpy(dest, src, n);
+ }
+ else {
+ return dest;
+ }
+}
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_MEMORY_H */
diff --git a/include/ruby/internal/method.h b/include/ruby/internal/method.h
new file mode 100644
index 0000000000..19feb0c10b
--- /dev/null
+++ b/include/ruby/internal/method.h
@@ -0,0 +1,205 @@
+#ifndef RBIMPL_METHOD_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_METHOD_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Creation and modification of Ruby methods.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/backward/2/stdarg.h"
+
+/**
+ * @defgroup defmethod Defining methods
+ *
+ * There are some APIs to define a method from C.
+ * These API takes a C function as a method body.
+ *
+ * ### Method body functions
+ *
+ * Method body functions must return a VALUE and
+ * can be one of the following form:
+ *
+ * #### Fixed number of parameters
+ *
+ * This form is a normal C function, excepting it takes
+ * a receiver object as the first argument.
+ *
+ * ```CXX
+ * static VALUE my_method(VALUE self, VALUE x, VALUE y);
+ * ```
+ *
+ * #### argc and argv style
+ *
+ * This form takes three parameters: argc, argv and self.
+ * self is the receiver. argc is the number of arguments.
+ * argv is a pointer to an array of the arguments.
+ *
+ * ```CXX
+ * static VALUE my_method(int argc, VALUE *argv, VALUE self);
+ * ```
+ *
+ * #### Ruby array style
+ *
+ * This form takes two parameters: self and args.
+ * self is the receiver. args is an Array object which
+ * contains the arguments.
+ *
+ * ```CXX
+ * static VALUE my_method(VALUE self, VALUE args);
+ * ```
+ *
+ * ### Number of parameters
+ *
+ * Method defining APIs takes the number of parameters which the
+ * method will takes. This number is called argc.
+ * argc can be:
+ *
+ * - Zero or positive number.
+ * This means the method body function takes a fixed number of parameters.
+ *
+ * - `-1`.
+ * This means the method body function is "argc and argv" style.
+ *
+ * - `-2`.
+ * This means the method body function is "self and args" style.
+ *
+ * @{
+ */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines a method.
+ *
+ * @param[out] klass A module or a class.
+ * @param[in] mid Name of the function.
+ * @param[in] func The method body.
+ * @param[in] arity The number of parameters. See @ref defmethod.
+ * @note There are in fact 18 different prototypes for func.
+ * @see ::ruby::backward::cxxanyargs::define_method::rb_define_method
+ */
+void rb_define_method(VALUE klass, const char *mid, VALUE (*func)(ANYARGS), int arity);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines a module function for a module.
+ *
+ * @param[out] klass A module or a class.
+ * @param[in] mid Name of the function.
+ * @param[in] func The method body.
+ * @param[in] arity The number of parameters. See @ref defmethod.
+ * @note There are in fact 18 different prototypes for func.
+ * @see ::ruby::backward::cxxanyargs::define_method::rb_define_module_function
+ */
+void rb_define_module_function(VALUE klass, const char *mid, VALUE (*func)(ANYARGS), int arity);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines a global function.
+ *
+ * @param[in] mid Name of the function.
+ * @param[in] func The method body.
+ * @param[in] arity The number of parameters. See @ref defmethod.
+ * @note There are in fact 18 different prototypes for func.
+ * @see ::ruby::backward::cxxanyargs::define_method::rb_define_global_function
+ */
+void rb_define_global_function(const char *mid, VALUE (*func)(ANYARGS), int arity);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines an undef of a method. -- What?
+ *
+ * In ruby, there are two separate concepts called "undef" and "remove_method".
+ * The thing you imagine when you "un-define" a method is remove_method. This
+ * one on the other hand is masking of a previous method definition. Suppose
+ * for instance:
+ *
+ * ```ruby
+ * class Foo
+ * def foo
+ * end
+ * end
+ *
+ * class Bar < Foo
+ * def bar
+ * foo
+ * end
+ * end
+ *
+ * class Baz < Foo
+ * undef foo # <--- (*1)
+ * end
+ * ```
+ *
+ * This `undef foo` at `(*1)` must not eliminate `Foo#foo`, because that method
+ * is also used from `Bar#bar`. So instead of physically executing the target
+ * method, `undef` inserts a special filtering entry to the class (`Baz` this
+ * case). That entry, when called, acts as if there were no methods at all.
+ * But the original can still be accessible, via ways like `Bar#bar` above.
+ *
+ * @param[out] klass The class to insert an undef.
+ * @param[in] name Name of the undef.
+ * @exception rb_eTypeError `klass` is a non-module.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @see rb_remove_method
+ */
+void rb_undef_method(VALUE klass, const char *name);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines an alias of a method.
+ *
+ * @param[in,out] klass The class which the original method belongs
+ * to; this is also where the new method will
+ * belong to.
+ * @param[in] dst A new name for the method.
+ * @param[in] src The original name of the method.
+ * @exception rb_eTypeError `klass` is a non-module.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @exception rb_eNameError There is no such method named as `src` in
+ * `klass`.
+ *
+ * @internal
+ *
+ * Above description is in fact a bit inaccurate because it ignores
+ * Refinements.
+ */
+void rb_define_alias(VALUE klass, const char *dst, const char *src);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines public accessor method(s) for an attribute.
+ *
+ * @param[out] klass The class which the attribute will belong to.
+ * @param[in] name Name of the attribute.
+ * @param[in] read Whether to define a getter method.
+ * @param[in] write Whether to define a setter method.
+ * @exception rb_eTypeError `klass` is a non-module.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @exception rb_eNameError `name` invalid as an attr e.g. an operator.
+ */
+void rb_define_attr(VALUE klass, const char *name, int read, int write);
+
+/** @} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_METHOD_H */
diff --git a/include/ruby/internal/module.h b/include/ruby/internal/module.h
new file mode 100644
index 0000000000..97b0b2b8b0
--- /dev/null
+++ b/include/ruby/internal/module.h
@@ -0,0 +1,177 @@
+#ifndef RBIMPL_MODULE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_MODULE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Creation and modification of Ruby modules.
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+/**
+ * @defgroup class Classes and their hierarchy.
+ *
+ * @par Terminology
+ * - class: same as in Ruby.
+ * - singleton class: class for a particular object.
+ * - eigenclass: = singleton class
+ * - metaclass: class of a class. Metaclass is a kind of singleton class.
+ * - metametaclass: class of a metaclass.
+ * - meta^(n)-class: class of a meta^(n-1)-class.
+ * - attached object: A singleton class knows its unique instance.
+ * The instance is called the attached object for the singleton class.
+ * @{
+ */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines a top-level class.
+ *
+ * @param[in] name Name of the class.
+ * @param[in] super A class from which the new class will derive.
+ * @exception rb_eTypeError The constant name `name` is already taken but the
+ * constant is not a class.
+ * @exception rb_eTypeError The class is already defined but the class can
+ * not be reopened because its superclass is not
+ * `super`.
+ * @exception rb_eArgError `super` is NULL.
+ * @return The created class.
+ * @post Top-level constant named `name` refers the returned class.
+ * @note If a class named `name` is already defined and its superclass is
+ * `super`, the function just returns the defined class.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ *
+ * @internal
+ *
+ * There are classes without names, but you can't pass NULL here. You have to
+ * use other ways to create one.
+ */
+VALUE rb_define_class(const char *name, VALUE super);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines a top-level module.
+ *
+ * @param[in] name Name of the module.
+ * @exception rb_eTypeError The constant name `name` is already taken but the
+ * constant is not a module.
+ * @return The created module.
+ * @post Top-level constant named `name` refers the returned module.
+ * @note The GC does not collect nor move modules returned by this
+ * function. They are immortal.
+ *
+ * @internal
+ *
+ * There are modules without names, but you can't pass NULL here. You have to
+ * use other ways to create one.
+ */
+VALUE rb_define_module(const char *name);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines a class under the namespace of `outer`.
+ *
+ * @param[out] outer A class which contains the new class.
+ * @param[in] name Name of the new class
+ * @param[in] super A class from which the new class will derive.
+ * 0 means ::rb_cObject.
+ * @exception rb_eTypeError The constant name `name` is already taken but
+ * the constant is not a class.
+ * @exception rb_eTypeError The class is already defined but the class can
+ * not be reopened because its superclass is not
+ * `super`.
+ * @exception rb_eArgError `super` is NULL.
+ * @return The created class.
+ * @post `outer::name` refers the returned class.
+ * @note If a class named `name` is already defined and its superclass
+ * is `super`, the function just returns the defined class.
+ * @note The GC does not collect nor move classes returned by this
+ * function. They are immortal.
+ */
+VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines a module under the namespace of `outer`.
+ *
+ * @param[out] outer A class which contains the new module.
+ * @param[in] name Name of the new module
+ * @exception rb_eTypeError The constant name `name` is already taken but
+ * the constant is not a class.
+ * @return The created module.
+ * @post `outer::name` refers the returned module.
+ * @note The GC does not collect nor move modules returned by this
+ * function. They are immortal.
+ */
+VALUE rb_define_module_under(VALUE outer, const char *name);
+
+/**
+ * Includes a module to a class.
+ *
+ * @param[out] klass Inclusion destination.
+ * @param[in] module Inclusion source.
+ * @exception rb_eArgError Cyclic inclusion.
+ *
+ * @internal
+ *
+ * :FIXME: @shyouhei suspects this function lacks assertion that the arguments
+ * being modules... Could silently SEGV if non-module was passed?
+ */
+void rb_include_module(VALUE klass, VALUE module);
+
+/**
+ * Extend the object with the module.
+ *
+ * @warning This is the same as `Module#extend_object`, not
+ * `Object#extend`! These two methods are very similar, but not
+ * identical. The difference is the hook. `Module#extend_object`
+ * does not invoke `Module#extended`, while `Object#extend` does.
+ * @param[out] obj Object to extend.
+ * @param[in] mod Module of extension.
+ */
+void rb_extend_object(VALUE obj, VALUE mod);
+
+/**
+ * Identical to rb_include_module(), except it "prepends" the passed module to
+ * the klass, instead of includes. This affects how `super` resolves. For
+ * instance:
+ *
+ * ```ruby
+ * class Q; def foo; "<q/>" end end
+ * module W; def foo; "<w>#{super}</w>" end end
+ * class E < Q; include W; def foo; "<e>#{super}</e>" end end
+ * class R < Q; prepend W; def foo; "<r>#{super}</r>" end end
+ *
+ * E.new.foo # => "<e><w><q/></w></e>"
+ * r.new.foo # => "<W><r><q/></r></w>"
+ * ```
+ *
+ * @param[out] klass Target class to modify.
+ * @param[in] module Module to prepend.
+ * @exception rb_eArgError Cyclic inclusion.
+ */
+void rb_prepend_module(VALUE klass, VALUE module);
+
+/** @} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_MODULE_H */
diff --git a/include/ruby/internal/newobj.h b/include/ruby/internal/newobj.h
new file mode 100644
index 0000000000..13030ae279
--- /dev/null
+++ b/include/ruby/internal/newobj.h
@@ -0,0 +1,112 @@
+#ifndef RBIMPL_NEWOBJ_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_NEWOBJ_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #NEWOBJ.
+ */
+#include "ruby/internal/attr/deprecated.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/internal/value.h"
+#include "ruby/assert.h"
+
+#define OBJSETUP rb_obj_setup /**< @old{rb_obj_setup} */
+#define CLONESETUP rb_clone_setup /**< @old{rb_clone_setup} */
+#define DUPSETUP rb_dup_setup /**< @old{rb_dup_setup} */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * Fills common fields in the object.
+ *
+ * @param[in,out] obj A Ruby object to be set up.
+ * @param[in] klass `obj` will belong to this class.
+ * @param[in] type One of ::ruby_value_type.
+ * @return The passed object.
+ *
+ * @internal
+ *
+ * Historically, authors of Ruby has described the `type` argument as "one of
+ * ::ruby_value_type". In reality it accepts either ::ruby_value_type,
+ * ::ruby_fl_type, or any combinations of the two. For instance
+ * `RUBY_T_STRING | RUBY_FL_FREEZE` is a valid value that this function takes,
+ * and means this is a frozen string.
+ *
+ * 3rd party extension libraries rarely need to allocate Strings this way.
+ * They normally only concern ::RUBY_T_DATA. This argument is mainly used for
+ * specifying flags, @shyouhei suspects.
+ */
+VALUE rb_obj_setup(VALUE obj, VALUE klass, VALUE type);
+
+/**
+ * Queries the class of an object. This is not always identical to
+ * `RBASIC_CLASS(obj)`. It searches for the nearest ancestor skipping
+ * singleton classes or included modules.
+ *
+ * @param[in] obj Object in question.
+ * @return The object's class, in a normal sense.
+ */
+VALUE rb_obj_class(VALUE obj);
+
+/**
+ * Clones a singleton class. An object can have its own singleton class. OK.
+ * Then what happens when a program clones such object? The singleton class
+ * that is attached to the source object must also be cloned. Otherwise a
+ * singleton object gets shared with two objects, which breaks "singleton"-ness
+ * of such class.
+ *
+ * This is basically an implementation detail of rb_clone_setup(). People
+ * need not be aware of this working behind-the-scene.
+ *
+ * @param[in] obj The object that has its own singleton class.
+ * @return Cloned singleton class.
+ */
+VALUE rb_singleton_class_clone(VALUE obj);
+
+/**
+ * Attaches a singleton class to its corresponding object.
+ *
+ * This is basically an implementation detail of rb_clone_setup(). People
+ * need not be aware of this working behind-the-scene.
+ *
+ * @param[in] klass The singleton class.
+ * @param[out] obj The object to attach a class.
+ * @pre The passed two objects must agree with each other that `klass`
+ * becomes a singleton class of `obj`.
+ * @post `klass` becomes the singleton class of `obj`.
+ */
+void rb_singleton_class_attached(VALUE klass, VALUE obj);
+
+/**
+ * Copies the list of instance variables. 3rd parties need not know, but there
+ * are several ways to store an object's instance variables, depending on its
+ * internal structure. This function makes sense when either of the passed
+ * objects are using so-called "generic" backend storage. This distinction is
+ * purely an implementation detail of rb_clone_setup(). People need not be
+ * aware of this working behind-the-scenes.
+ *
+ * @param[out] clone The destination object.
+ * @param[in] obj The source object.
+ */
+void rb_copy_generic_ivar(VALUE clone, VALUE obj);
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_NEWOBJ_H */
diff --git a/include/ruby/internal/scan_args.h b/include/ruby/internal/scan_args.h
new file mode 100644
index 0000000000..2dbc1ee7bc
--- /dev/null
+++ b/include/ruby/internal/scan_args.h
@@ -0,0 +1,534 @@
+#ifndef RBIMPL_SCAN_ARGS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_SCAN_ARGS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Compile-time static implementation of ::rb_scan_args().
+ *
+ * This is a beast. It statically analyses the argument spec string, and
+ * expands the assignment of variables into dedicated codes.
+ */
+#include "ruby/assert.h"
+#include "ruby/internal/attr/diagnose_if.h"
+#include "ruby/internal/attr/error.h"
+#include "ruby/internal/attr/forceinline.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/config.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/intern/array.h" /* rb_ary_new_from_values */
+#include "ruby/internal/intern/error.h" /* rb_error_arity */
+#include "ruby/internal/intern/hash.h" /* rb_hash_dup */
+#include "ruby/internal/intern/proc.h" /* rb_block_proc */
+#include "ruby/internal/iterator.h" /* rb_block_given_p / rb_keyword_given_p */
+#include "ruby/internal/static_assert.h"
+#include "ruby/internal/stdbool.h"
+#include "ruby/internal/value.h"
+
+/**
+ * @name Possible values that you should pass to rb_scan_args_kw().
+ * @{
+ */
+
+/** Same behaviour as rb_scan_args(). */
+#define RB_SCAN_ARGS_PASS_CALLED_KEYWORDS 0
+
+/** The final argument should be a hash treated as keywords.*/
+#define RB_SCAN_ARGS_KEYWORDS 1
+
+/**
+ * Treat a final argument as keywords if it is a hash, and not as keywords
+ * otherwise.
+ */
+#define RB_SCAN_ARGS_LAST_HASH_KEYWORDS 3
+
+/** @} */
+
+/**
+ * @name Possible values that you should pass to rb_funcallv_kw().
+ * @{
+ */
+
+/** Do not pass keywords. */
+#define RB_NO_KEYWORDS 0
+
+/** Pass keywords, final argument should be a hash of keywords. */
+#define RB_PASS_KEYWORDS 1
+
+/**
+ * Pass keywords if current method is called with keywords, useful for argument
+ * delegation
+ */
+#define RB_PASS_CALLED_KEYWORDS !!rb_keyword_given_p()
+
+/** @} */
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define HAVE_RB_SCAN_ARGS_OPTIONAL_HASH 1
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_NONNULL((2, 3))
+/**
+ * Retrieves argument from argc and argv to given ::VALUE references according
+ * to the format string. The format can be described in ABNF as follows:
+ *
+ * ```
+ * scan-arg-spec := param-arg-spec [keyword-arg-spec] [block-arg-spec]
+ *
+ * param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec /
+ * pre-opt-post-arg-spec
+ * pre-arg-spec := num-of-leading-mandatory-args
+ * [num-of-optional-args]
+ * post-arg-spec := sym-for-variable-length-args
+ * [num-of-trailing-mandatory-args]
+ * pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args
+ * num-of-trailing-mandatory-args
+ * keyword-arg-spec := sym-for-keyword-arg
+ * block-arg-spec := sym-for-block-arg
+ *
+ * num-of-leading-mandatory-args := DIGIT ; The number of leading mandatory
+ * ; arguments
+ * num-of-optional-args := DIGIT ; The number of optional arguments
+ * sym-for-variable-length-args := "*" ; Indicates that variable length
+ * ; arguments are captured as a ruby
+ * ; array
+ * num-of-trailing-mandatory-args := DIGIT ; The number of trailing mandatory
+ * ; arguments
+ * sym-for-keyword-arg := ":" ; Indicates that keyword argument
+ * ; captured as a hash.
+ * ; If keyword arguments are not
+ * ; provided, returns nil.
+ * sym-for-block-arg := "&" ; Indicates that an iterator block
+ * ; should be captured if given
+ * ```
+ *
+ * For example, "12" means that the method requires at least one argument, and
+ * at most receives three (1+2) arguments. So, the format string must be
+ * followed by three variable references, which are to be assigned to captured
+ * arguments. For omitted arguments, variables are set to ::RUBY_Qnil. `NULL`
+ * can be put in place of a variable reference, which means the corresponding
+ * captured argument(s) should be just dropped.
+ *
+ * The number of given arguments, excluding an option hash or iterator block,
+ * is returned.
+ *
+ * @param[in] argc Length of `argv`.
+ * @param[in] argv Pointer to the arguments to parse.
+ * @param[in] fmt Format, in the language described above.
+ * @param[out] ... Variables to fill in.
+ * @exception rb_eFatal Malformed `fmt`.
+ * @exception rb_eArgError Arity mismatch.
+ * @return Actually parsed number of given arguments.
+ * @post Each values passed to `argv` is filled into the variadic
+ * arguments, according to the format.
+ */
+int rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((3, 4))
+/**
+ * Identical to rb_scan_args(), except it also accepts `kw_splat`.
+ *
+ * @param[in] kw_splat How to understand the keyword arguments.
+ * - RB_SCAN_ARGS_PASS_CALLED_KEYWORDS: Same behaviour as rb_scan_args().
+ * - RB_SCAN_ARGS_KEYWORDS: The final argument is a kwarg.
+ * - RB_SCAN_ARGS_LAST_HASH_KEYWORDS: The final argument is a kwarg, iff it
+ * is a hash.
+ * @param[in] argc Length of `argv`.
+ * @param[in] argv Pointer to the arguments to parse.
+ * @param[in] fmt Format, in the language described above.
+ * @param[out] ... Variables to fill in.
+ * @exception rb_eFatal Malformed `fmt`.
+ * @exception rb_eArgError Arity mismatch.
+ * @return Actually parsed number of given arguments.
+ * @post Each values passed to `argv` is filled into the variadic
+ * arguments, according to the format.
+ */
+int rb_scan_args_kw(int kw_splat, int argc, const VALUE *argv, const char *fmt, ...);
+
+RBIMPL_ATTR_ERROR(("bad scan arg format"))
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_scan_args(). People don't use it
+ * directly.
+ */
+void rb_scan_args_bad_format(const char*);
+
+RBIMPL_ATTR_ERROR(("variable argument length doesn't match"))
+/**
+ * @private
+ *
+ * This is an implementation detail of rb_scan_args(). People don't use it
+ * directly.
+ */
+void rb_scan_args_length_mismatch(const char*,int);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/** @cond INTERNAL_MACRO */
+
+/* If we could use constexpr the following macros could be inline functions
+ * ... but sadly we cannot. */
+
+#define rb_scan_args_isdigit(c) (RBIMPL_CAST((unsigned char)((c)-'0'))<10)
+
+#define rb_scan_args_count_end(fmt, ofs, vari) \
+ ((fmt)[ofs] ? -1 : (vari))
+
+#define rb_scan_args_count_block(fmt, ofs, vari) \
+ ((fmt)[ofs]!='&' ? \
+ rb_scan_args_count_end(fmt, ofs, vari) : \
+ rb_scan_args_count_end(fmt, (ofs)+1, (vari)+1))
+
+#define rb_scan_args_count_hash(fmt, ofs, vari) \
+ ((fmt)[ofs]!=':' ? \
+ rb_scan_args_count_block(fmt, ofs, vari) : \
+ rb_scan_args_count_block(fmt, (ofs)+1, (vari)+1))
+
+#define rb_scan_args_count_trail(fmt, ofs, vari) \
+ (!rb_scan_args_isdigit((fmt)[ofs]) ? \
+ rb_scan_args_count_hash(fmt, ofs, vari) : \
+ rb_scan_args_count_hash(fmt, (ofs)+1, (vari)+((fmt)[ofs]-'0')))
+
+#define rb_scan_args_count_var(fmt, ofs, vari) \
+ ((fmt)[ofs]!='*' ? \
+ rb_scan_args_count_trail(fmt, ofs, vari) : \
+ rb_scan_args_count_trail(fmt, (ofs)+1, (vari)+1))
+
+#define rb_scan_args_count_opt(fmt, ofs, vari) \
+ (!rb_scan_args_isdigit((fmt)[ofs]) ? \
+ rb_scan_args_count_var(fmt, ofs, vari) : \
+ rb_scan_args_count_var(fmt, (ofs)+1, (vari)+(fmt)[ofs]-'0'))
+
+#define rb_scan_args_count_lead(fmt, ofs, vari) \
+ (!rb_scan_args_isdigit((fmt)[ofs]) ? \
+ rb_scan_args_count_var(fmt, ofs, vari) : \
+ rb_scan_args_count_opt(fmt, (ofs)+1, (vari)+(fmt)[ofs]-'0'))
+
+#define rb_scan_args_count(fmt) rb_scan_args_count_lead(fmt, 0, 0)
+
+#if RBIMPL_HAS_ATTRIBUTE(diagnose_if)
+# /* Assertions done in the attribute. */
+# define rb_scan_args_verify(fmt, varc) RBIMPL_ASSERT_NOTHING
+#else
+# /* At one sight it _seems_ the expressions below could be written using
+# * static assertions. The reality is no, they don't. Because fmt is a
+# * string literal, any operations against fmt cannot produce the "integer
+# * constant expression"s, as defined in ISO/IEC 9899:2018 section 6.6
+# * paragraph #6. Static assertions need such integer constant expressions as
+# * defined in ISO/IEC 9899:2018 section 6.7.10 paragraph #3.
+# *
+# * GCC nonetheless constant-folds this into a no-op, though. */
+# define rb_scan_args_verify(fmt, varc) \
+ (sizeof(char[1-2*(rb_scan_args_count(fmt)<0)])!=1 ? \
+ rb_scan_args_bad_format(fmt) : \
+ sizeof(char[1-2*(rb_scan_args_count(fmt)!=(varc))])!=1 ? \
+ rb_scan_args_length_mismatch(fmt, varc) : \
+ RBIMPL_ASSERT_NOTHING)
+#endif
+
+static inline bool
+rb_scan_args_keyword_p(int kw_flag, VALUE last)
+{
+ switch (kw_flag) {
+ case RB_SCAN_ARGS_PASS_CALLED_KEYWORDS:
+ return !! rb_keyword_given_p();
+ case RB_SCAN_ARGS_KEYWORDS:
+ return true;
+ case RB_SCAN_ARGS_LAST_HASH_KEYWORDS:
+ return RB_TYPE_P(last, T_HASH);
+ default:
+ return false;
+ }
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static bool
+rb_scan_args_lead_p(const char *fmt)
+{
+ return rb_scan_args_isdigit(fmt[0]);
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_n_lead(const char *fmt)
+{
+ return (rb_scan_args_lead_p(fmt) ? fmt[0]-'0' : 0);
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static bool
+rb_scan_args_opt_p(const char *fmt)
+{
+ return (rb_scan_args_lead_p(fmt) && rb_scan_args_isdigit(fmt[1]));
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_n_opt(const char *fmt)
+{
+ return (rb_scan_args_opt_p(fmt) ? fmt[1]-'0' : 0);
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_var_idx(const char *fmt)
+{
+ return (!rb_scan_args_lead_p(fmt) ? 0 : !rb_scan_args_isdigit(fmt[1]) ? 1 : 2);
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static bool
+rb_scan_args_f_var(const char *fmt)
+{
+ return (fmt[rb_scan_args_var_idx(fmt)]=='*');
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_trail_idx(const char *fmt)
+{
+ const int idx = rb_scan_args_var_idx(fmt);
+ return idx+(fmt[idx]=='*');
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_n_trail(const char *fmt)
+{
+ const int idx = rb_scan_args_trail_idx(fmt);
+ return (rb_scan_args_isdigit(fmt[idx]) ? fmt[idx]-'0' : 0);
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_hash_idx(const char *fmt)
+{
+ const int idx = rb_scan_args_trail_idx(fmt);
+ return idx+rb_scan_args_isdigit(fmt[idx]);
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static bool
+rb_scan_args_f_hash(const char *fmt)
+{
+ return (fmt[rb_scan_args_hash_idx(fmt)]==':');
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_block_idx(const char *fmt)
+{
+ const int idx = rb_scan_args_hash_idx(fmt);
+ return idx+(fmt[idx]==':');
+}
+
+RBIMPL_ATTR_FORCEINLINE()
+static bool
+rb_scan_args_f_block(const char *fmt)
+{
+ return (fmt[rb_scan_args_block_idx(fmt)]=='&');
+}
+
+# if 0
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_end_idx(const char *fmt)
+{
+ const int idx = rb_scan_args_block_idx(fmt);
+ return idx+(fmt[idx]=='&');
+}
+# endif
+
+/* NOTE: Use `char *fmt` instead of `const char *fmt` because of clang's bug*/
+/* https://bugs.llvm.org/show_bug.cgi?id=38095 */
+# define rb_scan_args0(argc, argv, fmt, varc, vars) \
+ rb_scan_args_set(RB_SCAN_ARGS_PASS_CALLED_KEYWORDS, argc, argv, \
+ rb_scan_args_n_lead(fmt), \
+ rb_scan_args_n_opt(fmt), \
+ rb_scan_args_n_trail(fmt), \
+ rb_scan_args_f_var(fmt), \
+ rb_scan_args_f_hash(fmt), \
+ rb_scan_args_f_block(fmt), \
+ (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc)
+# define rb_scan_args_kw0(kw_flag, argc, argv, fmt, varc, vars) \
+ rb_scan_args_set(kw_flag, argc, argv, \
+ rb_scan_args_n_lead(fmt), \
+ rb_scan_args_n_opt(fmt), \
+ rb_scan_args_n_trail(fmt), \
+ rb_scan_args_f_var(fmt), \
+ rb_scan_args_f_hash(fmt), \
+ rb_scan_args_f_block(fmt), \
+ (rb_scan_args_verify(fmt, varc), vars), (char *)fmt, varc)
+
+RBIMPL_ATTR_FORCEINLINE()
+static int
+rb_scan_args_set(int kw_flag, int argc, const VALUE *argv,
+ int n_lead, int n_opt, int n_trail,
+ bool f_var, bool f_hash, bool f_block,
+ VALUE *vars[], RB_UNUSED_VAR(const char *fmt), RB_UNUSED_VAR(int varc))
+ RBIMPL_ATTR_DIAGNOSE_IF(rb_scan_args_count(fmt) < 0, "bad scan arg format", "error")
+ RBIMPL_ATTR_DIAGNOSE_IF(rb_scan_args_count(fmt) != varc, "variable argument length doesn't match", "error")
+{
+ int i, argi = 0, vari = 0;
+ VALUE *var, hash = Qnil;
+#define rb_scan_args_next_param() vars[vari++]
+ const int n_mand = n_lead + n_trail;
+
+ /* capture an option hash - phase 1: pop from the argv */
+ if (f_hash && argc > 0) {
+ VALUE last = argv[argc - 1];
+ if (rb_scan_args_keyword_p(kw_flag, last)) {
+ hash = rb_hash_dup(last);
+ argc--;
+ }
+ }
+
+ if (argc < n_mand) {
+ goto argc_error;
+ }
+
+ /* capture leading mandatory arguments */
+ for (i = 0; i < n_lead; i++) {
+ var = rb_scan_args_next_param();
+ if (var) *var = argv[argi];
+ argi++;
+ }
+
+ /* capture optional arguments */
+ for (i = 0; i < n_opt; i++) {
+ var = rb_scan_args_next_param();
+ if (argi < argc - n_trail) {
+ if (var) *var = argv[argi];
+ argi++;
+ }
+ else {
+ if (var) *var = Qnil;
+ }
+ }
+
+ /* capture variable length arguments */
+ if (f_var) {
+ int n_var = argc - argi - n_trail;
+
+ var = rb_scan_args_next_param();
+ if (0 < n_var) {
+ if (var) *var = rb_ary_new_from_values(n_var, &argv[argi]);
+ argi += n_var;
+ }
+ else {
+ if (var) *var = rb_ary_new();
+ }
+ }
+
+ /* capture trailing mandatory arguments */
+ for (i = 0; i < n_trail; i++) {
+ var = rb_scan_args_next_param();
+ if (var) *var = argv[argi];
+ argi++;
+ }
+
+ /* capture an option hash - phase 2: assignment */
+ if (f_hash) {
+ var = rb_scan_args_next_param();
+ if (var) *var = hash;
+ }
+
+ /* capture iterator block */
+ if (f_block) {
+ var = rb_scan_args_next_param();
+ if (rb_block_given_p()) {
+ *var = rb_block_proc();
+ }
+ else {
+ *var = Qnil;
+ }
+ }
+
+ if (argi == argc) {
+ return argc;
+ }
+
+ argc_error:
+ rb_error_arity(argc, n_mand, f_var ? UNLIMITED_ARGUMENTS : n_mand + n_opt);
+ UNREACHABLE_RETURN(-1);
+#undef rb_scan_args_next_param
+}
+
+/** @endcond */
+
+#if defined(__DOXYGEN__)
+# /* don't bother */
+
+#elif ! defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
+# /* skip */
+
+#elif ! defined(HAVE_VA_ARGS_MACRO)
+# /* skip */
+
+#elif ! defined(__OPTIMIZE__)
+# /* skip */
+
+#elif defined(HAVE___VA_OPT__)
+# define rb_scan_args(argc, argvp, fmt, ...) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(fmt), \
+ rb_scan_args0( \
+ argc, argvp, fmt, \
+ (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
+ ((VALUE*[]){__VA_ARGS__})), \
+ (rb_scan_args)(argc, argvp, fmt __VA_OPT__(, __VA_ARGS__)))
+# define rb_scan_args_kw(kw_flag, argc, argvp, fmt, ...) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(fmt), \
+ rb_scan_args_kw0( \
+ kw_flag, argc, argvp, fmt, \
+ (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
+ ((VALUE*[]){__VA_ARGS__})), \
+ (rb_scan_args_kw)(kw_flag, argc, argvp, fmt __VA_OPT__(, __VA_ARGS__)))
+
+#elif defined(__STRICT_ANSI__)
+# /* skip */
+
+#elif defined(__GNUC__)
+# define rb_scan_args(argc, argvp, fmt, ...) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(fmt), \
+ rb_scan_args0( \
+ argc, argvp, fmt, \
+ (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
+ ((VALUE*[]){__VA_ARGS__})), \
+ (rb_scan_args)(argc, argvp, fmt, __VA_ARGS__))
+# define rb_scan_args_kw(kw_flag, argc, argvp, fmt, ...) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(fmt), \
+ rb_scan_args_kw0( \
+ kw_flag, argc, argvp, fmt, \
+ (sizeof((VALUE*[]){__VA_ARGS__})/sizeof(VALUE*)), \
+ ((VALUE*[]){__VA_ARGS__})), \
+ (rb_scan_args_kw)(kw_flag, argc, argvp, fmt, __VA_ARGS__ /**/))
+#endif
+
+#endif /* RBIMPL_SCAN_ARGS_H */
diff --git a/include/ruby/internal/special_consts.h b/include/ruby/internal/special_consts.h
new file mode 100644
index 0000000000..1e2636da48
--- /dev/null
+++ b/include/ruby/internal/special_consts.h
@@ -0,0 +1,362 @@
+#ifndef RBIMPL_SPECIAL_CONSTS_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_SPECIAL_CONSTS_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines enum ::ruby_special_consts.
+ * @see Sasada, K., "A Lightweight Representation of Floating-Point
+ * Numbers on Ruby Interpreter", in proceedings of 10th JSSST
+ * SIGPPL Workshop on Programming and Programming Languages
+ * (PPL2008), pp. 9-16, 2008.
+ */
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/constexpr.h"
+#include "ruby/internal/attr/enum_extensibility.h"
+#include "ruby/internal/stdbool.h"
+#include "ruby/internal/value.h"
+
+/**
+ * @private
+ * @warning Do not touch this macro.
+ * @warning It is an implementation detail.
+ * @warning The value of this macro must match for ruby itself and all
+ * extension libraries, otherwise serious memory corruption shall
+ * occur.
+ */
+#if defined(USE_FLONUM)
+# /* Take that. */
+#elif SIZEOF_VALUE >= SIZEOF_DOUBLE
+# define USE_FLONUM 1
+#else
+# define USE_FLONUM 0
+#endif
+
+/** This is an old name of #RB_TEST. Not sure which name is preferred. */
+#define RTEST RB_TEST
+
+#define FIXNUM_P RB_FIXNUM_P /**< @old{RB_FIXNUM_P} */
+#define IMMEDIATE_P RB_IMMEDIATE_P /**< @old{RB_IMMEDIATE_P} */
+#define NIL_P RB_NIL_P /**< @old{RB_NIL_P} */
+#define SPECIAL_CONST_P RB_SPECIAL_CONST_P /**< @old{RB_SPECIAL_CONST_P} */
+#define STATIC_SYM_P RB_STATIC_SYM_P /**< @old{RB_STATIC_SYM_P} */
+
+#define Qfalse RUBY_Qfalse /**< @old{RUBY_Qfalse} */
+#define Qnil RUBY_Qnil /**< @old{RUBY_Qnil} */
+#define Qtrue RUBY_Qtrue /**< @old{RUBY_Qtrue} */
+#define Qundef RUBY_Qundef /**< @old{RUBY_Qundef} */
+
+#define FIXNUM_FLAG RUBY_FIXNUM_FLAG /**< @old{RUBY_FIXNUM_FLAG} */
+#define FLONUM_FLAG RUBY_FLONUM_FLAG /**< @old{RUBY_FLONUM_FLAG} */
+#define FLONUM_MASK RUBY_FLONUM_MASK /**< @old{RUBY_FLONUM_MASK} */
+#define FLONUM_P RB_FLONUM_P /**< @old{RB_FLONUM_P} */
+#define IMMEDIATE_MASK RUBY_IMMEDIATE_MASK /**< @old{RUBY_IMMEDIATE_MASK} */
+#define SYMBOL_FLAG RUBY_SYMBOL_FLAG /**< @old{RUBY_SYMBOL_FLAG} */
+
+/** @cond INTERNAL_MACRO */
+#define RB_FIXNUM_P RB_FIXNUM_P
+#define RB_FLONUM_P RB_FLONUM_P
+#define RB_IMMEDIATE_P RB_IMMEDIATE_P
+#define RB_NIL_P RB_NIL_P
+#define RB_SPECIAL_CONST_P RB_SPECIAL_CONST_P
+#define RB_STATIC_SYM_P RB_STATIC_SYM_P
+#define RB_TEST RB_TEST
+#define RB_UNDEF_P RB_UNDEF_P
+#define RB_NIL_OR_UNDEF_P RB_NIL_OR_UNDEF_P
+/** @endcond */
+
+/** special constants - i.e. non-zero and non-fixnum constants */
+enum
+RBIMPL_ATTR_ENUM_EXTENSIBILITY(closed)
+ruby_special_consts {
+#if defined(__DOXYGEN__)
+ RUBY_Qfalse, /**< @see ::rb_cFalseClass */
+ RUBY_Qtrue, /**< @see ::rb_cTrueClass */
+ RUBY_Qnil, /**< @see ::rb_cNilClass */
+ RUBY_Qundef, /**< Represents so-called undef. */
+ RUBY_IMMEDIATE_MASK, /**< Bit mask detecting special consts. */
+ RUBY_FIXNUM_FLAG, /**< Flag to denote a fixnum. */
+ RUBY_FLONUM_MASK, /**< Bit mask detecting a flonum. */
+ RUBY_FLONUM_FLAG, /**< Flag to denote a flonum. */
+ RUBY_SYMBOL_FLAG, /**< Flag to denote a static symbol. */
+#elif USE_FLONUM
+ RUBY_Qfalse = 0x00, /* ...0000 0000 */
+ RUBY_Qnil = 0x04, /* ...0000 0100 */
+ RUBY_Qtrue = 0x14, /* ...0001 0100 */
+ RUBY_Qundef = 0x24, /* ...0010 0100 */
+ RUBY_IMMEDIATE_MASK = 0x07, /* ...0000 0111 */
+ RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */
+ RUBY_FLONUM_MASK = 0x03, /* ...0000 0011 */
+ RUBY_FLONUM_FLAG = 0x02, /* ...xxxx xx10 */
+ RUBY_SYMBOL_FLAG = 0x0c, /* ...xxxx 1100 */
+#else
+ RUBY_Qfalse = 0x00, /* ...0000 0000 */
+ RUBY_Qnil = 0x02, /* ...0000 0010 */
+ RUBY_Qtrue = 0x06, /* ...0000 0110 */
+ RUBY_Qundef = 0x0a, /* ...0000 1010 */
+ RUBY_IMMEDIATE_MASK = 0x03, /* ...0000 0011 */
+ RUBY_FIXNUM_FLAG = 0x01, /* ...xxxx xxx1 */
+ RUBY_FLONUM_MASK = 0x00, /* any values ANDed with FLONUM_MASK cannot be FLONUM_FLAG */
+ RUBY_FLONUM_FLAG = 0x02, /* ...0000 0010 */
+ RUBY_SYMBOL_FLAG = 0x0e, /* ...xxxx 1110 */
+#endif
+
+ RUBY_SPECIAL_SHIFT = 8 /**< Least significant 8 bits are reserved. */
+};
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Emulates Ruby's "if" statement.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval false `obj` is either ::RUBY_Qfalse or ::RUBY_Qnil.
+ * @retval true Anything else.
+ *
+ * @internal
+ *
+ * It HAS to be `__attribute__((const))` in order for clang to properly deduce
+ * `__builtin_assume()`.
+ */
+static inline bool
+RB_TEST(VALUE obj)
+{
+ /*
+ * if USE_FLONUM
+ * Qfalse: ....0000 0000
+ * Qnil: ....0000 0100
+ * ~Qnil: ....1111 1011
+ * v ....xxxx xxxx
+ * ----------------------------
+ * RTEST(v) ....xxxx x0xx
+ *
+ * if ! USE_FLONUM
+ * Qfalse: ....0000 0000
+ * Qnil: ....0000 0010
+ * ~Qnil: ....1111 1101
+ * v ....xxxx xxxx
+ * ----------------------------
+ * RTEST(v) ....xxxx xx0x
+ *
+ * RTEST(v) can be 0 if and only if (v == Qfalse || v == Qnil).
+ */
+ return obj & RBIMPL_CAST((VALUE)~RUBY_Qnil);
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is nil.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is ::RUBY_Qnil.
+ * @retval false Anything else.
+ */
+static inline bool
+RB_NIL_P(VALUE obj)
+{
+ return obj == RUBY_Qnil;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is undef.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is ::RUBY_Qundef.
+ * @retval false Anything else.
+ */
+static inline bool
+RB_UNDEF_P(VALUE obj)
+{
+ return obj == RUBY_Qundef;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX14)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is nil or undef. Can be used to see if
+ * a keyword argument is not given or given `nil`.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is ::RUBY_Qnil or ::RUBY_Qundef.
+ * @retval false Anything else.
+ */
+static inline bool
+RB_NIL_OR_UNDEF_P(VALUE obj)
+{
+ /*
+ * if USE_FLONUM
+ * Qundef: ....0010 0100
+ * Qnil: ....0000 0100
+ * mask: ....1101 1111
+ * common_bits: ....0000 0100
+ * ---------------------------------
+ * Qnil & mask ....0000 0100
+ * Qundef & mask ....0000 0100
+ *
+ * if ! USE_FLONUM
+ * Qundef: ....0000 1010
+ * Qnil: ....0000 0010
+ * mask: ....1111 0111
+ * common_bits: ....0000 0010
+ * ----------------------------
+ * Qnil & mask ....0000 0010
+ * Qundef & mask ....0000 0010
+ *
+ * NIL_OR_UNDEF_P(v) can be true only when v is Qundef or Qnil.
+ */
+ const VALUE mask = RBIMPL_CAST((VALUE)~(RUBY_Qundef ^ RUBY_Qnil));
+ const VALUE common_bits = RUBY_Qundef & RUBY_Qnil;
+ return (obj & mask) == common_bits;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is a so-called Fixnum.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is a Fixnum.
+ * @retval false Anything else.
+ * @note Fixnum was a thing in the 20th century, but it is rather an
+ * implementation detail today.
+ */
+static inline bool
+RB_FIXNUM_P(VALUE obj)
+{
+ return obj & RUBY_FIXNUM_FLAG;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX14)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is a static symbol.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is a static symbol
+ * @retval false Anything else.
+ * @see RB_DYNAMIC_SYM_P()
+ * @see RB_SYMBOL_P()
+ * @note These days there are static and dynamic symbols, just like we
+ * once had Fixnum/Bignum back in the old days.
+ */
+static inline bool
+RB_STATIC_SYM_P(VALUE obj)
+{
+ RBIMPL_ATTR_CONSTEXPR(CXX14)
+ const VALUE mask = ~(RBIMPL_VALUE_FULL << RUBY_SPECIAL_SHIFT);
+ return (obj & mask) == RUBY_SYMBOL_FLAG;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is a so-called Flonum.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is a Flonum.
+ * @retval false Anything else.
+ * @see RB_FLOAT_TYPE_P()
+ * @note These days there are Flonums and non-Flonum floats, just like we
+ * once had Fixnum/Bignum back in the old days.
+ */
+static inline bool
+RB_FLONUM_P(VALUE obj)
+{
+#if USE_FLONUM
+ return (obj & RUBY_FLONUM_MASK) == RUBY_FLONUM_FLAG;
+#else
+ return false;
+#endif
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is an immediate i.e. an object which has no
+ * corresponding storage inside of the object space.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is a Flonum.
+ * @retval false Anything else.
+ * @see RB_FLOAT_TYPE_P()
+ * @note The concept of "immediate" is purely C specific.
+ */
+static inline bool
+RB_IMMEDIATE_P(VALUE obj)
+{
+ return obj & RUBY_IMMEDIATE_MASK;
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Checks if the given object is of enum ::ruby_special_consts.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval true `obj` is a special constant.
+ * @retval false Anything else.
+ */
+static inline bool
+RB_SPECIAL_CONST_P(VALUE obj)
+{
+ return (obj == RUBY_Qfalse) || RB_IMMEDIATE_P(obj);
+}
+
+RBIMPL_ATTR_CONST()
+RBIMPL_ATTR_CONSTEXPR(CXX11)
+/**
+ * Identical to RB_SPECIAL_CONST_P, except it returns a ::VALUE.
+ *
+ * @param[in] obj An arbitrary ruby object.
+ * @retval RUBY_Qtrue `obj` is a special constant.
+ * @retval RUBY_Qfalse Anything else.
+ *
+ * @internal
+ *
+ * This function is to mimic old rb_special_const_p macro but have anyone
+ * actually used its return value? Wasn't it just something no one needed?
+ */
+static inline VALUE
+rb_special_const_p(VALUE obj)
+{
+ return (unsigned int)RB_SPECIAL_CONST_P(obj) * RUBY_Qtrue;
+}
+
+/**
+ * @cond INTERNAL_MACRO
+ * See [ruby-dev:27513] for the following macros.
+ */
+#define RUBY_Qfalse RBIMPL_CAST((VALUE)RUBY_Qfalse)
+#define RUBY_Qtrue RBIMPL_CAST((VALUE)RUBY_Qtrue)
+#define RUBY_Qnil RBIMPL_CAST((VALUE)RUBY_Qnil)
+#define RUBY_Qundef RBIMPL_CAST((VALUE)RUBY_Qundef)
+/** @endcond */
+
+#endif /* RBIMPL_SPECIAL_CONSTS_H */
diff --git a/include/ruby/internal/static_assert.h b/include/ruby/internal/static_assert.h
new file mode 100644
index 0000000000..30bfd3bb79
--- /dev/null
+++ b/include/ruby/internal/static_assert.h
@@ -0,0 +1,78 @@
+#ifndef RBIMPL_STATIC_ASSERT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_STATIC_ASSERT_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_STATIC_ASSERT.
+ */
+#include <assert.h>
+#include "ruby/internal/has/extension.h"
+#include "ruby/internal/compiler_since.h"
+#include "ruby/internal/attr/maybe_unused.h"
+
+/** @cond INTERNAL_MACRO */
+#if defined(__cplusplus) && defined(__cpp_static_assert)
+# /* https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations */
+# define RBIMPL_STATIC_ASSERT0 static_assert
+
+#elif defined(__cplusplus) && RBIMPL_COMPILER_IS(MSVC)
+# define RBIMPL_STATIC_ASSERT0 static_assert
+
+#elif defined(__INTEL_CXX11_MODE__)
+# define RBIMPL_STATIC_ASSERT0 static_assert
+
+#elif defined(__cplusplus) && __cplusplus >= 201103L
+# define RBIMPL_STATIC_ASSERT0 static_assert
+
+#elif defined(__cplusplus) && RBIMPL_HAS_EXTENSION(cxx_static_assert)
+# define RBIMPL_STATIC_ASSERT0 __extension__ static_assert
+
+#elif defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__
+# define RBIMPL_STATIC_ASSERT0 __extension__ static_assert
+
+#elif defined(__STDC_VERSION__) && RBIMPL_HAS_EXTENSION(c_static_assert)
+# define RBIMPL_STATIC_ASSERT0 __extension__ _Static_assert
+
+#elif defined(__STDC_VERSION__) && RBIMPL_COMPILER_SINCE(GCC, 4, 6, 0)
+# define RBIMPL_STATIC_ASSERT0 __extension__ _Static_assert
+
+#elif defined(static_assert)
+# /* Take <assert.h> definition */
+# define RBIMPL_STATIC_ASSERT0 static_assert
+#endif
+/** @endcond */
+
+/**
+ * @brief Wraps (or simulates) `static_assert`
+ * @param name Valid C/C++ identifier, describing the assertion.
+ * @param expr Expression to assert.
+ * @note `name` shall not be a string literal.
+ */
+#if defined(__DOXYGEN__)
+# define RBIMPL_STATIC_ASSERT static_assert
+
+#elif defined(RBIMPL_STATIC_ASSERT0)
+# define RBIMPL_STATIC_ASSERT(name, expr) \
+ RBIMPL_STATIC_ASSERT0(expr, # name ": " # expr)
+
+#else
+# define RBIMPL_STATIC_ASSERT(name, expr) \
+ RBIMPL_ATTR_MAYBE_UNUSED() typedef int static_assert_ ## name ## _check[1 - 2 * !(expr)]
+#endif
+
+#endif /* RBIMPL_STATIC_ASSERT_H */
diff --git a/include/ruby/internal/stdalign.h b/include/ruby/internal/stdalign.h
new file mode 100644
index 0000000000..ec68f6a882
--- /dev/null
+++ b/include/ruby/internal/stdalign.h
@@ -0,0 +1,135 @@
+#ifndef RBIMPL_STDALIGN_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_STDALIGN_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_ALIGNAS / #RBIMPL_ALIGNOF
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#include "ruby/internal/compiler_is.h"
+#include "ruby/internal/has/attribute.h"
+#include "ruby/internal/has/declspec_attribute.h"
+#include "ruby/internal/has/feature.h"
+
+/**
+ * Wraps (or simulates) `alignas`. This is C++11's `alignas` and is _different_
+ * from C11 `_Alignas`. For instance,
+ *
+ * ```CXX
+ * typedef struct alignas(128) foo { int foo } foo;
+ * ```
+ *
+ * is a valid C++ while
+ *
+ * ```C
+ * typedef struct _Alignas(128) foo { int foo } foo;
+ * ```
+ *
+ * is an invalid C because:
+ *
+ * - You cannot `struct _Alignas`.
+ * - A `typedef` cannot have alignments.
+ */
+#if defined(__cplusplus) && RBIMPL_HAS_FEATURE(cxx_alignas)
+# define RBIMPL_ALIGNAS alignas
+
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+# define RBIMPL_ALIGNAS alignas
+
+#elif defined(__INTEL_CXX11_MODE__)
+# define RBIMPL_ALIGNAS alignas
+
+#elif defined(__GXX_EXPERIMENTAL_CXX0X__)
+# define RBIMPL_ALIGNAS alignas
+
+#elif RBIMPL_HAS_DECLSPEC_ATTRIBUTE(align)
+# define RBIMPL_ALIGNAS(_) __declspec(align(_))
+
+#elif RBIMPL_HAS_ATTRIBUTE(aligned)
+# define RBIMPL_ALIGNAS(_) __attribute__((__aligned__(_)))
+
+#else
+# define RBIMPL_ALIGNAS(_) /* void */
+#endif
+
+/**
+ * Wraps (or simulates) `alignof`.
+ *
+ * We want C11's `_Alignof`. However in spite of its clear language, compilers
+ * (including GCC and clang) tend to have buggy implementations. We have to
+ * avoid such things to resort to our own version.
+ *
+ * @see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023
+ * @see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69560
+ * @see https://bugs.llvm.org/show_bug.cgi?id=26547
+ */
+#if defined(__DOXYGEN__)
+# define RBIMPL_ALIGNOF alignof
+#elif defined(__cplusplus)
+# /* C++11 `alignof()` can be buggy. */
+# /* see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69560 */
+# /* But don't worry, we can use templates. */
+# define RBIMPL_ALIGNOF(T) (static_cast<size_t>(ruby::rbimpl_alignof<T>::value))
+
+namespace ruby {
+template<typename T>
+struct rbimpl_alignof {
+ typedef struct {
+ char _;
+ T t;
+ } type;
+
+ enum {
+ value = offsetof(type, t)
+ };
+};
+}
+
+#elif RBIMPL_COMPILER_IS(MSVC)
+# /* Windows have no alignment glitch.*/
+# define RBIMPL_ALIGNOF __alignof
+
+#elif defined(HAVE__ALIGNOF)
+# /* Autoconf detected availability of a sane `_Alignof()`. */
+# define RBIMPL_ALIGNOF(T) RB_GNUC_EXTENSION(_Alignof(T))
+
+#else
+# /* :BEWARE: This is the last resort. If your compiler somehow supports
+# * querying the alignment of a type, you definitely should use that instead.
+# * There are 2 known pitfalls for this fallback implementation:
+# *
+# * First, it is either an undefined behaviour (C) or an explicit error (C++)
+# * to define a struct inside of `offsetof`. C compilers tend to accept such
+# * things, but AFAIK C++ has no room to allow.
+# *
+# * Second, there exist T such that `struct { char _; T t; }` is invalid. A
+# * known example is when T is a struct with a flexible array member. Such
+# * struct cannot be enclosed into another one.
+# */
+# /* see: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2083.htm */
+# /* see: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2350.htm */
+# define RBIMPL_ALIGNOF(T) offsetof(struct { char _; T t; }, t)
+
+#endif
+
+#endif /* RBIMPL_STDALIGN_H */
diff --git a/include/ruby/internal/stdbool.h b/include/ruby/internal/stdbool.h
new file mode 100644
index 0000000000..5d9026434b
--- /dev/null
+++ b/include/ruby/internal/stdbool.h
@@ -0,0 +1,39 @@
+#ifndef RBIMPL_STDBOOL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_STDBOOL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief C99 shim for <stdbool.h>
+ */
+#include "ruby/internal/config.h"
+
+#if defined(__bool_true_false_are_defined)
+# /* Take that. */
+
+#elif defined(__cplusplus)
+# /* bool is a keyword in C++. */
+# ifndef __bool_true_false_are_defined
+# define __bool_true_false_are_defined
+# endif
+
+#else
+# /* Take stdbool.h definition. It exists since GCC 3.0 and VS 2015. */
+# include <stdbool.h>
+#endif
+
+#endif /* RBIMPL_STDBOOL_H */
diff --git a/include/ruby/internal/stdckdint.h b/include/ruby/internal/stdckdint.h
new file mode 100644
index 0000000000..e5b5b8b751
--- /dev/null
+++ b/include/ruby/internal/stdckdint.h
@@ -0,0 +1,68 @@
+#ifndef RBIMPL_STDCKDINT_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_STDCKDINT_H
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief C23 shim for <stdckdint.h>
+ */
+#include "ruby/internal/config.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/has/builtin.h"
+#include "ruby/internal/stdbool.h"
+
+#ifdef __has_include
+# if __has_include(<stdckdint.h>)
+# /* Conforming C23 situation; e.g. recent clang */
+# define RBIMPL_HAVE_STDCKDINT_H
+# endif
+#endif
+
+#ifdef HAVE_STDCKDINT_H
+# /* Some OSes (most notably FreeBSD) have this file. */
+# define RBIMPL_HAVE_STDCKDINT_H
+#endif
+
+#ifdef __cplusplus
+# /* It seems OS/Compiler provided stdckdint.h tend not support C++ yet.
+# * Situations could improve someday but in a meantime let us work around.
+# */
+# undef RBIMPL_HAVE_STDCKDINT_H
+#endif
+
+#ifdef RBIMPL_HAVE_STDCKDINT_H
+# /* Take that. */
+# include <stdckdint.h>
+
+#elif RBIMPL_HAS_BUILTIN(__builtin_add_overflow)
+# define ckd_add(x, y, z) RBIMPL_CAST((bool)__builtin_add_overflow((y), (z), (x)))
+# define ckd_sub(x, y, z) RBIMPL_CAST((bool)__builtin_sub_overflow((y), (z), (x)))
+# define ckd_mul(x, y, z) RBIMPL_CAST((bool)__builtin_mul_overflow((y), (z), (x)))
+# define __STDC_VERSION_STDCKDINT_H__ 202311L
+
+#/* elif defined(__cplusplus) */
+#/* :TODO: if we assume C++11 we can use `<type_traits>` to implement them. */
+
+#else
+# /* intentionally leave them undefined */
+# /* to make `#ifdef ckd_add` etc. work as intended. */
+# undef ckd_add
+# undef ckd_sub
+# undef ckd_mul
+# undef __STDC_VERSION_STDCKDINT_H__
+#endif
+
+#endif /* RBIMPL_STDCKDINT_H */
diff --git a/include/ruby/internal/symbol.h b/include/ruby/internal/symbol.h
new file mode 100644
index 0000000000..8bfd686fbe
--- /dev/null
+++ b/include/ruby/internal/symbol.h
@@ -0,0 +1,343 @@
+#ifndef RBIMPL_SYMBOL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_SYMBOL_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #rb_intern
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/constant_p.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/has/builtin.h"
+#include "ruby/internal/value.h"
+
+#define RB_ID2SYM rb_id2sym /**< @alias{rb_id2sym} */
+#define RB_SYM2ID rb_sym2id /**< @alias{rb_sym2id} */
+#define ID2SYM RB_ID2SYM /**< @old{RB_ID2SYM} */
+#define SYM2ID RB_SYM2ID /**< @old{RB_SYM2ID} */
+#define CONST_ID_CACHE RUBY_CONST_ID_CACHE /**< @old{RUBY_CONST_ID_CACHE} */
+#define CONST_ID RUBY_CONST_ID /**< @old{RUBY_CONST_ID} */
+
+/** @cond INTERNAL_MACRO */
+#define rb_intern_const rb_intern_const
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Converts an instance of ::rb_cSymbol into an ::ID.
+ *
+ * @param[in] obj An instance of ::rb_cSymbol.
+ * @exception rb_eTypeError `obj` is not an instance of ::rb_cSymbol.
+ * @return An ::ID of the identical symbol.
+ */
+ID rb_sym2id(VALUE obj);
+
+/**
+ * Allocates an instance of ::rb_cSymbol that has the given id.
+ *
+ * @param[in] id An id.
+ * @retval RUBY_Qfalse No such id ever existed in the history.
+ * @retval Otherwise An allocated ::rb_cSymbol instance.
+ */
+VALUE rb_id2sym(ID id);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Finds or creates a symbol of the given name.
+ *
+ * @param[in] name The name of the id.
+ * @exception rb_eRuntimeError Too many symbols.
+ * @return A (possibly new) id whose value is the given name.
+ * @note These days Ruby internally has two kinds of symbols (static /
+ * dynamic). Symbols created using this function would become a
+ * static one; i.e. would never be garbage collected. It is up to
+ * you to avoid memory leaks. Think twice before using it.
+ */
+ID rb_intern(const char *name);
+
+/**
+ * Identical to rb_intern(), except it additionally takes the length of the
+ * string. This way you can have a symbol that contains NUL characters.
+ *
+ * @param[in] name The name of the id.
+ * @param[in] len Length of `name`.
+ * @exception rb_eRuntimeError Too many symbols.
+ * @return A (possibly new) id whose value is the given name.
+ * @note These days Ruby internally has two kinds of symbols
+ * (static/dynamic). Symbols created using this function would
+ * become static ones; i.e. would never be garbage collected. It
+ * is up to you to avoid memory leaks. Think twice before using
+ * it.
+ */
+ID rb_intern2(const char *name, long len);
+
+/**
+ * Identical to rb_intern(), except it takes a `T_STRING` object.
+ *
+ * @param[in] str The name of the id.
+ * @pre `rb_type(str)` must be `T_STRING`.
+ * @exception rb_eEncodingError `str` contains invalid character(s).
+ * @exception rb_eRuntimeError Too many symbols.
+ * @return A (possibly new) id whose value is the given str.
+ * @note These days Ruby internally has two kinds of symbols
+ * (static/dynamic). Symbols created using this function would
+ * become static ones; i.e. would never be garbage collected. It
+ * is up to you to avoid memory leaks. Think twice before using
+ * it.
+ */
+ID rb_intern_str(VALUE str);
+
+/**
+ * Retrieves the name mapped to the given id.
+ *
+ * @param[in] id An id to query.
+ * @retval NULL Unknown id.
+ * @retval otherwise A name that the id represents.
+ * @note The return value is managed by the interpreter. Don't pass it
+ * to free().
+ * @note The underlying name can contain internal NUL bytes, so the return
+ * value might be a truncated representation due to the nature of C
+ * strings.
+ * @note This C string is backed by an underlying Ruby string. The Ruby
+ * string may move during GC compaction which would make this
+ * C string point to invalid memory. Do not use the return value
+ * of this function after a potential GC entry point.
+ */
+const char *rb_id2name(ID id);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Detects if the given name is already interned or not. It first tries to
+ * convert the argument to an instance of ::rb_cString if it is neither an
+ * instance of ::rb_cString nor ::rb_cSymbol. The conversion result is written
+ * back to the variable. Then queries if that name was already interned
+ * before. If found it returns such id, otherwise zero.
+ *
+ * We eventually introduced this API to avoid inadvertent symbol pin-down.
+ * Before, there was no way to know if an ID was already interned or not
+ * without actually creating one (== leaking memory). By using this API you
+ * can avoid such situations:
+ *
+ * ```CXX
+ * bool does_interning_this_leak_memory(VALUE obj)
+ * {
+ * auto tmp = obj;
+ * if (auto id = rb_check_id(&tmp); id) {
+ * return false;
+ * }
+ * else {
+ * return true; // Let GC sweep tmp if necessary.
+ * }
+ * }
+ * ```
+ *
+ * @param[in,out] namep A pointer to a name to query.
+ * @pre The object referred by `*namep` must either be an instance
+ * of ::rb_cSymbol, or an instance of ::rb_cString, or responds
+ * to `#to_str` method.
+ * @exception rb_eTypeError Can't convert `*namep` into ::rb_cString.
+ * @exception rb_eEncodingError Given string contains invalid character(s).
+ * @retval 0 No such id ever existed in the history.
+ * @retval otherwise The id that represents the given name.
+ * @post The object that `*namep` points to is a converted result
+ * object, which is always an instance of either ::rb_cSymbol
+ * or ::rb_cString.
+ * @see rb_str_to_str
+ * @see https://bugs.ruby-lang.org/issues/5072
+ */
+ID rb_check_id(volatile VALUE *namep);
+
+/**
+ * Identical to rb_intern_str(), except it tries to convert the parameter object
+ * to an instance of ::rb_cString or its subclasses.
+ *
+ * @param[in] str The name of the id.
+ * @pre `str` must either be an instance of ::rb_cSymbol, or an instance
+ * of ::rb_cString, or responds to `#to_str` method.
+ * @exception rb_eTypeError Can't convert `str` into ::rb_cString.
+ * @exception rb_eEncodingError Given string contains invalid character(s).
+ * @exception rb_eRuntimeError Too many symbols.
+ * @return A (possibly new) id whose value is the given str.
+ * @note These days Ruby internally has two kinds of symbols
+ * (static/dynamic). Symbols created using this function would
+ * become static ones; i.e. would never be garbage collected. It
+ * is up to you to avoid memory leaks. Think twice before using
+ * it.
+ * @see rb_str_to_str
+ */
+ID rb_to_id(VALUE str);
+
+/**
+ * Identical to rb_id2name(), except it returns a frozen Ruby String instead of
+ * a C String.
+ *
+ * @param[in] id An id to query.
+ * @retval RUBY_Qfalse No such id ever existed in the history.
+ * @retval otherwise An instance of ::rb_cString with the name of id.
+ *
+ * @internal
+ *
+ * In reality "rb_id2str() is identical to rb_id2name() except it returns Ruby
+ * string" is just describing things upside down; truth is `rb_id2name(foo)` is
+ * a shorthand of `RSTRING_PTR(rb_id2str(foo))`.
+ */
+VALUE rb_id2str(ID id);
+
+/**
+ * Obtain a frozen string representation of a symbol (not including the leading
+ * colon). Done without any object allocations.
+ *
+ * @param[in] symbol A ::rb_cSymbol instance to query.
+ * @return A frozen instance of ::rb_cString with the name of `symbol`.
+ * @note This does not create a permanent ::ID using the symbol.
+ */
+VALUE rb_sym2str(VALUE symbol);
+
+/**
+ * Identical to rb_intern_str(), except it generates a dynamic symbol if
+ * necessary.
+ *
+ * @param[in] name The name of the id.
+ * @pre `name` must either be an instance of ::rb_cSymbol, or an
+ * instance of ::rb_cString, or responds to `#to_str` method.
+ * @exception rb_eTypeError Can't convert `name` into ::rb_cString.
+ * @exception rb_eRuntimeError Too many symbols.
+ * @return A (possibly new) id whose value is the given name.
+ * @note These days Ruby internally has two kinds of symbols
+ * (static/dynamic). Symbols created using this function would
+ * become dynamic ones; i.e. would be garbage collected. It could
+ * be safer for you to use it than alternatives, when applicable.
+ */
+VALUE rb_to_symbol(VALUE name);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_check_id(), except it returns an instance of ::rb_cSymbol
+ * instead.
+ *
+ * @param[in,out] namep A pointer to a name to query.
+ * @pre The object referred by `*namep` must either be an instance
+ * of ::rb_cSymbol, or an instance of ::rb_cString, or responds
+ * to `#to_str` method.
+ * @exception rb_eTypeError Can't convert `*namep` into ::rb_cString.
+ * @exception rb_eEncodingError Given string contains invalid character(s).
+ * @retval RUBY_Qnil No such id ever existed in the history.
+ * @retval otherwise The id that represents the given name.
+ * @post The object that `*namep` points to is a converted result
+ * object, which is always an instance of either ::rb_cSymbol
+ * or ::rb_cString.
+ * @see https://bugs.ruby-lang.org/issues/5072
+ * @see rb_str_to_str
+ */
+VALUE rb_check_symbol(volatile VALUE *namep);
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is a "tiny optimisation" over rb_intern(). If you pass a string
+ * _literal_, and if your C compiler can special-case strlen of such literal to
+ * strength-reduce into an integer constant expression, then this inline
+ * function can precalc a part of conversion.
+ *
+ * @note This function also works happily for non-constant strings. Why
+ * bother then? Just apply liberally to everything.
+ * @note But #rb_intern() could be faster on compilers with statement
+ * expressions, because they can cache the created ::ID.
+ * @param[in] str The name of the id.
+ * @exception rb_eRuntimeError Too many symbols.
+ * @return A (possibly new) id whose value is the given str.
+ * @note These days Ruby internally has two kinds of symbols (static /
+ * dynamic). Symbols created using this function would become a
+ * static one; i.e. would never be garbage collected. It is up to
+ * you to avoid memory leaks. Think twice before using it.
+ */
+static inline ID
+rb_intern_const(const char *str)
+{
+ size_t len = strlen(str);
+ return rb_intern2(str, RBIMPL_CAST((long)len));
+}
+
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * @private
+ *
+ * This is an implementation detail of #rb_intern(). Just don't use it.
+ */
+static inline ID
+rbimpl_intern_const(ID *ptr, const char *str)
+{
+ while (! *ptr) {
+ *ptr = rb_intern_const(str);
+ }
+
+ return *ptr;
+}
+
+/**
+ * Old implementation detail of rb_intern().
+ * @deprecated Does anyone use it? Preserved for backward compat.
+ */
+#define RUBY_CONST_ID_CACHE(result, str) \
+ { \
+ static ID rb_intern_id_cache; \
+ rbimpl_intern_const(&rb_intern_id_cache, (str)); \
+ result rb_intern_id_cache; \
+ }
+
+/**
+ * Returns the cached ID for the given str in var, in compiler
+ * independent manner. Use this instead of GCC specific rb_intern()
+ * when you want to cache the ID on all platforms certainly.
+ */
+#define RUBY_CONST_ID(var, str) \
+ do { \
+ static ID rbimpl_id; \
+ (var) = rbimpl_intern_const(&rbimpl_id, (str)); \
+ } while (0)
+
+#if defined(HAVE_STMT_AND_DECL_IN_EXPR)
+/* GCC specific shorthand for RUBY_CONST_ID().
+ * __builtin_constant_p and statement expression is available
+ * since gcc-2.7.2.3 at least. */
+#define rb_intern(str) \
+ (RBIMPL_CONSTANT_P(str) ? \
+ __extension__ ({ \
+ static ID rbimpl_id; \
+ rbimpl_intern_const(&rbimpl_id, (str)); \
+ }) : \
+ (rb_intern)(str))
+#endif
+
+#endif /* RBIMPL_SYMBOL_H */
diff --git a/include/ruby/internal/value.h b/include/ruby/internal/value.h
new file mode 100644
index 0000000000..805cd83513
--- /dev/null
+++ b/include/ruby/internal/value.h
@@ -0,0 +1,133 @@
+#ifndef RBIMPL_VALUE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_VALUE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines ::VALUE and ::ID.
+ */
+#include "ruby/internal/static_assert.h"
+#include "ruby/backward/2/long_long.h"
+#include "ruby/backward/2/limits.h"
+
+#if defined(__DOXYGEN__)
+
+/**
+ * Type that represents a Ruby object. It is an unsigned integer of some kind,
+ * depending on platforms.
+ *
+ * ```CXX
+ * VALUE value = rb_eval_string("ARGF.readlines.map.with_index");
+ * ```
+ *
+ * @warning ::VALUE is not a pointer.
+ * @warning ::VALUE can be wider than `long`.
+ */
+typedef uintptr_t VALUE;
+
+/**
+ * Type that represents a Ruby identifier such as a variable name.
+ *
+ * ```CXX
+ * ID method = rb_intern("method");
+ * VALUE result = rb_funcall(obj, method, 0);
+ * ```
+ *
+ * @note ::rb_cSymbol is a Ruby-level data type for the same thing.
+ */
+typedef uintptr_t ID;
+
+/**
+ * A signed integer type that has the same width with ::VALUE.
+ *
+ * @internal
+ *
+ * @shyouhei wonders: is it guaranteed that `uintptr_t` and `intptr_t` are the
+ * same width? As far as I read ISO/IEC 9899:2018 section 7.20.1.4 paragraph 1
+ * no such description is given... or defined elsewhere?
+ */
+typedef intptr_t SIGNED_VALUE;
+
+/**
+ * Identical to `sizeof(VALUE)`, except it is a macro that can also be used
+ * inside of preprocessor directives such as `#if`. Handy on occasions.
+ */
+#define SIZEOF_VALUE SIZEOF_UINTPTR_T
+
+/**
+ * @private
+ *
+ * A compile-time constant of type ::VALUE whose value is 0.
+ */
+#define RBIMPL_VALUE_NULL UINTPTR_C(0)
+
+/**
+ * @private
+ *
+ * A compile-time constant of type ::VALUE whose value is 1.
+ */
+#define RBIMPL_VALUE_ONE UINTPTR_C(1)
+
+/**
+ * @private
+ *
+ * Maximum possible value that a ::VALUE can take.
+ */
+#define RBIMPL_VALUE_FULL UINTPTR_MAX
+
+#elif defined HAVE_UINTPTR_T && 0
+typedef uintptr_t VALUE;
+typedef uintptr_t ID;
+# define SIGNED_VALUE intptr_t
+# define SIZEOF_VALUE SIZEOF_UINTPTR_T
+# undef PRI_VALUE_PREFIX
+# define RBIMPL_VALUE_NULL UINTPTR_C(0)
+# define RBIMPL_VALUE_ONE UINTPTR_C(1)
+# define RBIMPL_VALUE_FULL UINTPTR_MAX
+
+#elif SIZEOF_LONG == SIZEOF_VOIDP
+typedef unsigned long VALUE;
+typedef unsigned long ID;
+# define SIGNED_VALUE long
+# define SIZEOF_VALUE SIZEOF_LONG
+# define PRI_VALUE_PREFIX "l"
+# define RBIMPL_VALUE_NULL 0UL
+# define RBIMPL_VALUE_ONE 1UL
+# define RBIMPL_VALUE_FULL ULONG_MAX
+
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+typedef unsigned LONG_LONG VALUE;
+typedef unsigned LONG_LONG ID;
+# define SIGNED_VALUE LONG_LONG
+# define LONG_LONG_VALUE 1
+# define SIZEOF_VALUE SIZEOF_LONG_LONG
+# define PRI_VALUE_PREFIX PRI_LL_PREFIX
+# define RBIMPL_VALUE_NULL 0ULL
+# define RBIMPL_VALUE_ONE 1ULL
+# define RBIMPL_VALUE_FULL ULLONG_MAX
+
+#else
+# error ---->> ruby requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
+#endif
+
+/** @cond INTERNAL_MACRO */
+RBIMPL_STATIC_ASSERT(sizeof_int, SIZEOF_INT == sizeof(int));
+RBIMPL_STATIC_ASSERT(sizeof_long, SIZEOF_LONG == sizeof(long));
+RBIMPL_STATIC_ASSERT(sizeof_long_long, SIZEOF_LONG_LONG == sizeof(LONG_LONG));
+RBIMPL_STATIC_ASSERT(sizeof_voidp, SIZEOF_VOIDP == sizeof(void *));
+/** @endcond */
+#endif /* RBIMPL_VALUE_H */
diff --git a/include/ruby/internal/value_type.h b/include/ruby/internal/value_type.h
new file mode 100644
index 0000000000..b47d8afb97
--- /dev/null
+++ b/include/ruby/internal/value_type.h
@@ -0,0 +1,450 @@
+#ifndef RBIMPL_VALUE_TYPE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_VALUE_TYPE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines enum ::ruby_value_type.
+ */
+#include "ruby/internal/assume.h"
+#include "ruby/internal/attr/artificial.h"
+#include "ruby/internal/attr/cold.h"
+#include "ruby/internal/attr/enum_extensibility.h"
+#include "ruby/internal/attr/forceinline.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/cast.h"
+#include "ruby/internal/constant_p.h"
+#include "ruby/internal/core/rbasic.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/error.h"
+#include "ruby/internal/has/builtin.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/internal/stdbool.h"
+#include "ruby/internal/value.h"
+#include "ruby/assert.h"
+
+#if defined(T_DATA)
+/*
+ * :!BEWARE!: (Recent?) Solaris' <nfs/nfs.h> have conflicting definition of
+ * T_DATA. Let us stop here. Please have a workaround like this:
+ *
+ * ```C
+ * #include <ruby/ruby.h> // <- Include this one first.
+ * #undef T_DATA // <- ... and stick to RUBY_T_DATA forever.
+ * #include <nfs/nfs.h> // <- OS-provided T_DATA introduced.
+ * ```
+ *
+ * See also [ruby-core:4261]
+ */
+# error Bail out due to conflicting definition of T_DATA.
+#endif
+
+#define T_ARRAY RUBY_T_ARRAY /**< @old{RUBY_T_ARRAY} */
+#define T_BIGNUM RUBY_T_BIGNUM /**< @old{RUBY_T_BIGNUM} */
+#define T_CLASS RUBY_T_CLASS /**< @old{RUBY_T_CLASS} */
+#define T_COMPLEX RUBY_T_COMPLEX /**< @old{RUBY_T_COMPLEX} */
+#define T_DATA RUBY_T_DATA /**< @old{RUBY_T_DATA} */
+#define T_FALSE RUBY_T_FALSE /**< @old{RUBY_T_FALSE} */
+#define T_FILE RUBY_T_FILE /**< @old{RUBY_T_FILE} */
+#define T_FIXNUM RUBY_T_FIXNUM /**< @old{RUBY_T_FIXNUM} */
+#define T_FLOAT RUBY_T_FLOAT /**< @old{RUBY_T_FLOAT} */
+#define T_HASH RUBY_T_HASH /**< @old{RUBY_T_HASH} */
+#define T_ICLASS RUBY_T_ICLASS /**< @old{RUBY_T_ICLASS} */
+#define T_IMEMO RUBY_T_IMEMO /**< @old{RUBY_T_IMEMO} */
+#define T_MASK RUBY_T_MASK /**< @old{RUBY_T_MASK} */
+#define T_MATCH RUBY_T_MATCH /**< @old{RUBY_T_MATCH} */
+#define T_MODULE RUBY_T_MODULE /**< @old{RUBY_T_MODULE} */
+#define T_MOVED RUBY_T_MOVED /**< @old{RUBY_T_MOVED} */
+#define T_NIL RUBY_T_NIL /**< @old{RUBY_T_NIL} */
+#define T_NODE RUBY_T_NODE /**< @old{RUBY_T_NODE} */
+#define T_NONE RUBY_T_NONE /**< @old{RUBY_T_NONE} */
+#define T_OBJECT RUBY_T_OBJECT /**< @old{RUBY_T_OBJECT} */
+#define T_RATIONAL RUBY_T_RATIONAL /**< @old{RUBY_T_RATIONAL} */
+#define T_REGEXP RUBY_T_REGEXP /**< @old{RUBY_T_REGEXP} */
+#define T_STRING RUBY_T_STRING /**< @old{RUBY_T_STRING} */
+#define T_STRUCT RUBY_T_STRUCT /**< @old{RUBY_T_STRUCT} */
+#define T_SYMBOL RUBY_T_SYMBOL /**< @old{RUBY_T_SYMBOL} */
+#define T_TRUE RUBY_T_TRUE /**< @old{RUBY_T_TRUE} */
+#define T_UNDEF RUBY_T_UNDEF /**< @old{RUBY_T_UNDEF} */
+#define T_ZOMBIE RUBY_T_ZOMBIE /**< @old{RUBY_T_ZOMBIE} */
+
+#define BUILTIN_TYPE RB_BUILTIN_TYPE /**< @old{RB_BUILTIN_TYPE} */
+#define DYNAMIC_SYM_P RB_DYNAMIC_SYM_P /**< @old{RB_DYNAMIC_SYM_P} */
+#define RB_INTEGER_TYPE_P rb_integer_type_p /**< @old{rb_integer_type_p} */
+#define SYMBOL_P RB_SYMBOL_P /**< @old{RB_SYMBOL_P} */
+#define rb_type_p RB_TYPE_P /**< @alias{RB_TYPE_P} */
+
+/** @cond INTERNAL_MACRO */
+#define RB_BUILTIN_TYPE RB_BUILTIN_TYPE
+#define RB_DYNAMIC_SYM_P RB_DYNAMIC_SYM_P
+#define RB_FLOAT_TYPE_P RB_FLOAT_TYPE_P
+#define RB_SYMBOL_P RB_SYMBOL_P
+#define RB_TYPE_P RB_TYPE_P
+#define Check_Type Check_Type
+
+#ifdef RBIMPL_VA_OPT_ARGS
+# define RBIMPL_ASSERT_TYPE(v, t) \
+ RBIMPL_ASSERT_OR_ASSUME(RB_TYPE_P(v, t), "actual type: %d", rb_type(v))
+#else
+# define RBIMPL_ASSERT_TYPE(v, t) RBIMPL_ASSERT_OR_ASSUME(RB_TYPE_P(v, t))
+#endif
+/** @endcond */
+
+/** @old{rb_type} */
+#define TYPE(_) RBIMPL_CAST((int)rb_type(_))
+
+/** C-level type of an object. */
+enum
+RBIMPL_ATTR_ENUM_EXTENSIBILITY(closed)
+ruby_value_type {
+ RUBY_T_NONE = 0x00, /**< Non-object (swept etc.) */
+
+ RUBY_T_OBJECT = 0x01, /**< @see struct ::RObject */
+ RUBY_T_CLASS = 0x02, /**< @see struct ::RClass and ::rb_cClass */
+ RUBY_T_MODULE = 0x03, /**< @see struct ::RClass and ::rb_cModule */
+ RUBY_T_FLOAT = 0x04, /**< @see struct ::RFloat */
+ RUBY_T_STRING = 0x05, /**< @see struct ::RString */
+ RUBY_T_REGEXP = 0x06, /**< @see struct ::RRegexp */
+ RUBY_T_ARRAY = 0x07, /**< @see struct ::RArray */
+ RUBY_T_HASH = 0x08, /**< @see struct ::RHash */
+ RUBY_T_STRUCT = 0x09, /**< @see struct ::RStruct */
+ RUBY_T_BIGNUM = 0x0a, /**< @see struct ::RBignum */
+ RUBY_T_FILE = 0x0b, /**< @see struct ::RFile */
+ RUBY_T_DATA = 0x0c, /**< @see struct ::RTypedData */
+ RUBY_T_MATCH = 0x0d, /**< @see struct ::RMatch */
+ RUBY_T_COMPLEX = 0x0e, /**< @see struct ::RComplex */
+ RUBY_T_RATIONAL = 0x0f, /**< @see struct ::RRational */
+
+ RUBY_T_NIL = 0x11, /**< @see ::RUBY_Qnil */
+ RUBY_T_TRUE = 0x12, /**< @see ::RUBY_Qtrue */
+ RUBY_T_FALSE = 0x13, /**< @see ::RUBY_Qfalse */
+ RUBY_T_SYMBOL = 0x14, /**< @see struct ::RSymbol */
+ RUBY_T_FIXNUM = 0x15, /**< Integers formerly known as Fixnums. */
+ RUBY_T_UNDEF = 0x16, /**< @see ::RUBY_Qundef */
+
+ RUBY_T_IMEMO = 0x1a, /**< @see struct ::RIMemo */
+ RUBY_T_NODE = 0x1b, /**< @see struct ::RNode */
+ RUBY_T_ICLASS = 0x1c, /**< Hidden classes known as IClasses. */
+ RUBY_T_ZOMBIE = 0x1d, /**< @see struct ::RZombie */
+ RUBY_T_MOVED = 0x1e, /**< @see struct ::RMoved */
+
+ RUBY_T_MASK = 0x1f /**< Bitmask of ::ruby_value_type. */
+};
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+RBIMPL_ATTR_COLD()
+/**
+ * @private
+ *
+ * This was the old implementation of Check_Type(), but they diverged. This
+ * one remains for theoretical backwards compatibility. People normally need
+ * not use it.
+ *
+ * @param[in] obj An object.
+ * @param[in] t A type.
+ * @exception rb_eTypeError `obj` is not of type `t`.
+ * @exception rb_eFatal `obj` is corrupt.
+ * @post Upon successful return `obj` is guaranteed to have type `t`.
+ *
+ * @internal
+ *
+ * The second argument shall have been enum ::ruby_value_type. But at the time
+ * matz designed this function he still used K&R C. There was no such thing
+ * like a function prototype. We can no longer change this API.
+ */
+void rb_check_type(VALUE obj, int t);
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the type of the object.
+ *
+ * @param[in] obj Object in question.
+ * @pre `obj` must not be a special constant.
+ * @return The type of `obj`.
+ */
+static inline enum ruby_value_type
+RB_BUILTIN_TYPE(VALUE obj)
+{
+ RBIMPL_ASSERT_OR_ASSUME(! RB_SPECIAL_CONST_P(obj));
+
+#if 0 && defined __GNUC__ && !defined __clang__
+ /* Don't move the access to `flags` before the preceding
+ * RB_SPECIAL_CONST_P check. */
+ __asm volatile("": : :"memory");
+#endif
+ VALUE ret = RBASIC(obj)->flags & RUBY_T_MASK;
+ return RBIMPL_CAST((enum ruby_value_type)ret);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/**
+ * Queries if the object is an instance of ::rb_cInteger.
+ *
+ * @param[in] obj Object in question.
+ * @retval true It is.
+ * @retval false It isn't.
+ */
+static inline bool
+rb_integer_type_p(VALUE obj)
+{
+ if (RB_FIXNUM_P(obj)) {
+ return true;
+ }
+ else if (RB_SPECIAL_CONST_P(obj)) {
+ return false;
+ }
+ else {
+ return RB_BUILTIN_TYPE(obj) == RUBY_T_BIGNUM;
+ }
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/**
+ * Identical to RB_BUILTIN_TYPE(), except it can also accept special constants.
+ *
+ * @param[in] obj Object in question.
+ * @return The type of `obj`.
+ */
+static inline enum ruby_value_type
+rb_type(VALUE obj)
+{
+ if (! RB_SPECIAL_CONST_P(obj)) {
+ return RB_BUILTIN_TYPE(obj);
+ }
+ else if (obj == RUBY_Qfalse) {
+ return RUBY_T_FALSE;
+ }
+ else if (obj == RUBY_Qnil) {
+ return RUBY_T_NIL;
+ }
+ else if (obj == RUBY_Qtrue) {
+ return RUBY_T_TRUE;
+ }
+ else if (obj == RUBY_Qundef) {
+ return RUBY_T_UNDEF;
+ }
+ else if (RB_FIXNUM_P(obj)) {
+ return RUBY_T_FIXNUM;
+ }
+ else if (RB_STATIC_SYM_P(obj)) {
+ return RUBY_T_SYMBOL;
+ }
+ else {
+ RBIMPL_ASSUME(RB_FLONUM_P(obj));
+ return RUBY_T_FLOAT;
+ }
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries if the object is an instance of ::rb_cFloat.
+ *
+ * @param[in] obj Object in question.
+ * @retval true It is.
+ * @retval false It isn't.
+ */
+static inline bool
+RB_FLOAT_TYPE_P(VALUE obj)
+{
+ if (RB_FLONUM_P(obj)) {
+ return true;
+ }
+ else if (RB_SPECIAL_CONST_P(obj)) {
+ return false;
+ }
+ else {
+ return RB_BUILTIN_TYPE(obj) == RUBY_T_FLOAT;
+ }
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries if the object is a dynamic symbol.
+ *
+ * @param[in] obj Object in question.
+ * @retval true It is.
+ * @retval false It isn't.
+ */
+static inline bool
+RB_DYNAMIC_SYM_P(VALUE obj)
+{
+ if (RB_SPECIAL_CONST_P(obj)) {
+ return false;
+ }
+ else {
+ return RB_BUILTIN_TYPE(obj) == RUBY_T_SYMBOL;
+ }
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries if the object is an instance of ::rb_cSymbol.
+ *
+ * @param[in] obj Object in question.
+ * @retval true It is.
+ * @retval false It isn't.
+ */
+static inline bool
+RB_SYMBOL_P(VALUE obj)
+{
+ return RB_STATIC_SYM_P(obj) || RB_DYNAMIC_SYM_P(obj);
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+RBIMPL_ATTR_FORCEINLINE()
+/**
+ * @private
+ *
+ * This is an implementation detail of RB_TYPE_P(). Just don't use it.
+ *
+ * @param[in] obj An object.
+ * @param[in] t A type.
+ * @retval true `obj` is of type `t`.
+ * @retval false Otherwise.
+ */
+static bool
+rbimpl_RB_TYPE_P_fastpath(VALUE obj, enum ruby_value_type t)
+{
+ if (t == RUBY_T_TRUE) {
+ return obj == RUBY_Qtrue;
+ }
+ else if (t == RUBY_T_FALSE) {
+ return obj == RUBY_Qfalse;
+ }
+ else if (t == RUBY_T_NIL) {
+ return obj == RUBY_Qnil;
+ }
+ else if (t == RUBY_T_UNDEF) {
+ return obj == RUBY_Qundef;
+ }
+ else if (t == RUBY_T_FIXNUM) {
+ return RB_FIXNUM_P(obj);
+ }
+ else if (t == RUBY_T_SYMBOL) {
+ return RB_SYMBOL_P(obj);
+ }
+ else if (t == RUBY_T_FLOAT) {
+ return RB_FLOAT_TYPE_P(obj);
+ }
+ else if (RB_SPECIAL_CONST_P(obj)) {
+ return false;
+ }
+ else if (t == RB_BUILTIN_TYPE(obj)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries if the given object is of given type.
+ *
+ * @param[in] obj An object.
+ * @param[in] t A type.
+ * @retval true `obj` is of type `t`.
+ * @retval false Otherwise.
+ *
+ * @internal
+ *
+ * This function is a super-duper hot path. Optimised targeting modern C
+ * compilers and x86_64 architecture.
+ */
+static inline bool
+RB_TYPE_P(VALUE obj, enum ruby_value_type t)
+{
+ if (RBIMPL_CONSTANT_P(t)) {
+ return rbimpl_RB_TYPE_P_fastpath(obj, t);
+ }
+ else {
+ return t == rb_type(obj);
+ }
+}
+
+/** @cond INTERNAL_MACRO */
+/* Clang, unlike GCC, cannot propagate __builtin_constant_p beyond function
+ * boundary. */
+#if defined(__clang__)
+# undef RB_TYPE_P
+# define RB_TYPE_P(obj, t) \
+ (RBIMPL_CONSTANT_P(t) ? \
+ rbimpl_RB_TYPE_P_fastpath((obj), (t)) : \
+ (RB_TYPE_P)((obj), (t)))
+#endif
+
+/* clang 3.x (4.2 compatible) can't eliminate CSE of RB_BUILTIN_TYPE
+ * in inline function and caller function
+ * See also 8998c06461ea0bef11b3aeb30b6d2ab71c8762ba
+ */
+#if RBIMPL_COMPILER_BEFORE(Clang, 4, 0, 0)
+# undef rb_integer_type_p
+# define rb_integer_type_p(obj) \
+ __extension__ ({ \
+ const VALUE integer_type_obj = (obj); \
+ (RB_FIXNUM_P(integer_type_obj) || \
+ (!RB_SPECIAL_CONST_P(integer_type_obj) && \
+ RB_BUILTIN_TYPE(integer_type_obj) == RUBY_T_BIGNUM)); \
+ })
+#endif
+/** @endcond */
+
+RBIMPL_ATTR_PURE()
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * @private
+ * Defined in ruby/internal/core/rtypeddata.h
+ */
+static inline bool rbimpl_rtypeddata_p(VALUE obj);
+
+RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Identical to RB_TYPE_P(), except it raises exceptions on predication
+ * failure.
+ *
+ * @param[in] v An object.
+ * @param[in] t A type.
+ * @exception rb_eTypeError `obj` is not of type `t`.
+ * @exception rb_eFatal `obj` is corrupt.
+ * @post Upon successful return `obj` is guaranteed to have type `t`.
+ */
+static inline void
+Check_Type(VALUE v, enum ruby_value_type t)
+{
+ if (RB_UNLIKELY(! RB_TYPE_P(v, t))) {
+ goto unexpected_type;
+ }
+ else if (t == RUBY_T_DATA && rbimpl_rtypeddata_p(v)) {
+ /* Typed data is not simple `T_DATA`, see `rb_check_type` */
+ goto unexpected_type;
+ }
+ else {
+ return;
+ }
+
+ unexpected_type:
+ rb_unexpected_type(v, RBIMPL_CAST((int)t));
+}
+
+#endif /* RBIMPL_VALUE_TYPE_H */
diff --git a/include/ruby/internal/variable.h b/include/ruby/internal/variable.h
new file mode 100644
index 0000000000..c017ffe3f7
--- /dev/null
+++ b/include/ruby/internal/variable.h
@@ -0,0 +1,337 @@
+#ifndef RBIMPL_VARIABLE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_VARIABLE_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Declares rb_define_variable().
+ */
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/noreturn.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Type that represents a global variable getter function.
+ *
+ * @param[in] id The variable name.
+ * @param[in,out] data Where the value is stored.
+ * @return The value that shall be visible from Ruby.
+ */
+typedef VALUE rb_gvar_getter_t(ID id, VALUE *data);
+
+/**
+ * Type that represents a global variable setter function.
+ *
+ * @param[in] val The value to set.
+ * @param[in] id The variable name.
+ * @param[in,out] data Where the value is to be stored.
+ */
+typedef void rb_gvar_setter_t(VALUE val, ID id, VALUE *data);
+
+/**
+ * Type that represents a global variable marker function.
+ *
+ * @param[in] var Where the value is to be stored.
+ */
+typedef void rb_gvar_marker_t(VALUE *var);
+
+/**
+ * @deprecated
+ *
+ * This function has no actual usage (than in ruby itself). Please ignore. It
+ * was a bad idea to expose this function to 3rd parties, but we can no longer
+ * delete it.
+ */
+rb_gvar_getter_t rb_gvar_undef_getter;
+
+/**
+ * @deprecated
+ *
+ * This function has no actual usage (than in ruby itself). Please ignore. It
+ * was a bad idea to expose this function to 3rd parties, but we can no longer
+ * delete it.
+ */
+rb_gvar_setter_t rb_gvar_undef_setter;
+
+/**
+ * @deprecated
+ *
+ * This function has no actual usage (than in ruby itself). Please ignore. It
+ * was a bad idea to expose this function to 3rd parties, but we can no longer
+ * delete it.
+ */
+rb_gvar_marker_t rb_gvar_undef_marker;
+
+/**
+ * This is the getter function that backs global variables defined from a ruby
+ * script. Extension libraries can use this if its global variable needs no
+ * custom logic.
+ */
+rb_gvar_getter_t rb_gvar_val_getter;
+
+/**
+ * This is the setter function that backs global variables defined from a ruby
+ * script. Extension libraries can use this if its global variable needs no
+ * custom logic.
+ */
+rb_gvar_setter_t rb_gvar_val_setter;
+
+/**
+ * This is the setter function that backs global variables defined from a ruby
+ * script. Extension libraries can use this if its global variable needs no
+ * custom logic.
+ */
+rb_gvar_marker_t rb_gvar_val_marker;
+
+/**
+ * @deprecated
+ *
+ * This function has no actual usage (than in ruby itself). Please ignore. It
+ * was a bad idea to expose this function to 3rd parties, but we can no longer
+ * delete it.
+ */
+rb_gvar_getter_t rb_gvar_var_getter;
+
+/**
+ * @deprecated
+ *
+ * This function has no actual usage (than in ruby itself). Please ignore. It
+ * was a bad idea to expose this function to 3rd parties, but we can no longer
+ * delete it.
+ */
+rb_gvar_setter_t rb_gvar_var_setter;
+
+/**
+ * @deprecated
+ *
+ * This function has no actual usage (than in ruby itself). Please ignore. It
+ * was a bad idea to expose this function to 3rd parties, but we can no longer
+ * delete it.
+ */
+rb_gvar_marker_t rb_gvar_var_marker;
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * This function just raises ::rb_eNameError. Handy when you want to prohibit
+ * a global variable from being squashed by someone.
+ */
+rb_gvar_setter_t rb_gvar_readonly_setter;
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * "Shares" a global variable between Ruby and C. Normally a Ruby-level global
+ * variable is stored somewhere deep inside of the interpreter's execution
+ * context, but this way you can explicitly specify its storage.
+ *
+ * ```CXX
+ * static VALUE foo;
+ *
+ * extern "C" void
+ * init_Foo(void)
+ * {
+ * foo = rb_eval_string("...");
+ * rb_define_variable("$foo", &foo);
+ * }
+ * ```
+ *
+ * In the above example a Ruby global variable named `$foo` is stored in a C
+ * global variable named `foo`.
+ *
+ * @param[in] name Variable (Ruby side).
+ * @param[in] var Variable (C side).
+ * @post Ruby level global variable named `name` is defined if absent,
+ * and its storage is set to `var`.
+ */
+void rb_define_variable(const char *name, VALUE *var);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Defines a global variable that is purely function-backended. By using this
+ * API a programmer can define a global variable that dynamically changes from
+ * time to time.
+ *
+ * @param[in] name Variable name, in C's string.
+ * @param[in] getter A getter function.
+ * @param[in] setter A setter function.
+ * @post Ruby level global variable named `name` is defined if absent.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't know if this is an Easter egg or an official feature, but
+ * you can pass 0 to the third argument (setter). That effectively nullifies
+ * any efforts to write to the defining global variable.
+ */
+void rb_define_virtual_variable(const char *name, rb_gvar_getter_t *getter, rb_gvar_setter_t *setter);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_define_virtual_variable(), but can also specify a storage.
+ * A programmer can use the storage for e.g. memoisation, storing intermediate
+ * computation result, etc.
+ *
+ * Also you can pass 0 to this function, unlike other variants:
+ *
+ * - When getter is 0 ::rb_gvar_var_getter is used instead.
+ * - When setter is 0 ::rb_gvar_var_setter is used instead.
+ * - When data is 0, you must specify a non-zero setter function. Otherwise
+ * ::rb_gvar_var_setter tries to write to `*NULL`, and just causes SEGV.
+ *
+ * @param[in] name Variable name, in C's string.
+ * @param[in] var Variable storage.
+ * @param[in] getter A getter function.
+ * @param[in] setter A setter function.
+ * @post Ruby level global variable named `name` is defined if absent.
+ */
+void rb_define_hooked_variable(const char *name, VALUE *var, rb_gvar_getter_t *getter, rb_gvar_setter_t *setter);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_define_variable(), except it does not allow Ruby programs to
+ * assign values to such global variable. C codes can still set values at
+ * will. This could be handy for you when implementing an `errno`-like
+ * experience, where a method updates a read-only global variable as a side-
+ * effect.
+ *
+ * @param[in] name Variable (Ruby side).
+ * @param[in] var Variable (C side).
+ * @post Ruby level global variable named `name` is defined if absent,
+ * and its storage is set to `var`.
+ */
+void rb_define_readonly_variable(const char *name, const VALUE *var);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Defines a Ruby level constant under a namespace.
+ *
+ * @param[out] klass Namespace for the constant to reside.
+ * @param[in] name Name of the constant.
+ * @param[in] val Value of the constant.
+ * @exception rb_eTypeError `klass` is not a kind of ::rb_cModule.
+ * @exception rb_eFrozenError `klass` is frozen.
+ * @post Ruby level constant `klass::name` is defined to be `val`.
+ * @note This API does not stop you from defining a constant that is
+ * unable to reach from ruby (like for instance passing
+ * non-capital letter to `name`).
+ * @note This API does not stop you from overwriting a constant that
+ * already exist.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+void rb_define_const(VALUE klass, const char *name, VALUE val);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Identical to rb_define_const(), except it defines that of "global",
+ * i.e. toplevel constant.
+ *
+ * @param[in] name Name of the constant.
+ * @param[in] val Value of the constant.
+ * @exception rb_eFrozenError ::rb_cObject is frozen.
+ * @post Ruby level constant \::name is defined to be `val`.
+ * @note This API does not stop you from defining a constant that is
+ * unable to reach from ruby (like for instance passing
+ * non-capital letter to `name`).
+ * @note This API does not stop you from overwriting a constant that
+ * already exist.
+ */
+void rb_define_global_const(const char *name, VALUE val);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Asserts that the given constant is deprecated. Attempt to refer such
+ * constant will produce a warning.
+ *
+ * @param[in] mod Namespace of the target constant.
+ * @param[in] name Name of the constant.
+ * @exception rb_eNameError No such constant.
+ * @exception rb_eFrozenError `mod` is frozen.
+ * @post `name` under `mod` is deprecated.
+ */
+void rb_deprecate_constant(VALUE mod, const char *name);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Assigns to a global variable.
+ *
+ * @param[in] name Target global variable.
+ * @param[in] val Value to assign.
+ * @return Passed value.
+ * @post Ruby level global variable named `name` is defined if absent,
+ * whose value is set to `val`.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with
+ * `set_trace_func`.
+ */
+VALUE rb_gv_set(const char *name, VALUE val);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Obtains a global variable.
+ *
+ * @param[in] name Global variable to query.
+ * @retval RUBY_Qnil The global variable does not exist.
+ * @retval otherwise The value assigned to the global variable.
+ *
+ * @internal
+ *
+ * Unlike rb_gv_set(), there is no way to trace this function.
+ */
+VALUE rb_gv_get(const char *name);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Obtains an instance variable.
+ *
+ * @param[in] obj Target object.
+ * @param[in] name Target instance variable to query.
+ * @exception rb_eEncodingError `name` is corrupt (contains Hanzi etc.).
+ * @retval RUBY_nil No such instance variable.
+ * @retval otherwise The value assigned to the instance variable.
+ */
+VALUE rb_iv_get(VALUE obj, const char *name);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Assigns to an instance variable.
+ *
+ * @param[out] obj Target object.
+ * @param[in] name Target instance variable.
+ * @param[in] val Value to assign.
+ * @exception rb_eFrozenError Can't modify `obj`.
+ * @exception rb_eArgError `obj` has too many instance variables.
+ * @return Passed value.
+ * @post An instance variable named `name` is defined if absent on
+ * `obj`, whose value is set to `val`.
+ *
+ * @internal
+ *
+ * This function does not stop you form creating an ASCII-incompatible instance
+ * variable, but there is no way to get one because rb_iv_get raises exceptions
+ * for such things. This design seems broken... But no idea why.
+ */
+VALUE rb_iv_set(VALUE obj, const char *name, VALUE val);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_VARIABLE_H */
diff --git a/include/ruby/internal/warning_push.h b/include/ruby/internal/warning_push.h
new file mode 100644
index 0000000000..91d62cb00d
--- /dev/null
+++ b/include/ruby/internal/warning_push.h
@@ -0,0 +1,124 @@
+#ifndef RBIMPL_WARNING_PUSH_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_WARNING_PUSH_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Defines #RBIMPL_WARNING_PUSH.
+ *
+ * ### Q&A ###
+ *
+ * Q: Why all the macros defined in this file are function-like macros?
+ *
+ * A: Sigh. This is because of Doxygen. Its `SKIP_FUNCTION_MACROS = YES`
+ * configuration setting requests us that if we want it to ignore these
+ * macros, then we have to do two things: (1) let them be defined as
+ * function-like macros, and (2) place them separately in their own line,
+ * like below:
+ *
+ * ```CXX
+ * // NG -- foo's type considered something like `unsigned int`.
+ * RBIMPL_WARNING_PUSH
+ * int foo(void);
+ * RBIMPL_WARNING_POP
+ *
+ * // OK -- the macros are ignored by Doxygen.
+ * RBIMPL_WARNING_PUSH()
+ * int foo(void);
+ * RBIMPL_WARNING_POP()
+ * ```
+ */
+#include "ruby/internal/compiler_is.h"
+#include "ruby/internal/compiler_since.h"
+
+#if defined(__DOXYGEN__)
+
+/**
+ * @private
+ *
+ * Pushes compiler warning state.
+ */
+#define RBIMPL_WARNING_PUSH() __pragma(warning(push))
+
+/**
+ * @private
+ *
+ * Pops compiler warning state.
+ */
+#define RBIMPL_WARNING_POP() __pragma(warning(pop))
+
+/**
+ * @private
+ *
+ * Turns a warning into a fatal error.
+ *
+ * @param flag A flag that represents the kind of warnings.
+ */
+#define RBIMPL_WARNING_ERROR(flag) __pragma(warning(error: flag))
+
+/**
+ * @private
+ *
+ * Suppresses a warning.
+ *
+ * @param flag A flag that represents the kind of warnings.
+ */
+#define RBIMPL_WARNING_IGNORED(flag) __pragma(warning(disable: flag))
+
+#elif RBIMPL_COMPILER_IS(MSVC)
+# /* Not sure exactly when but it seems VC++ 6.0 is a version with it.*/
+# define RBIMPL_WARNING_PUSH() __pragma(warning(push))
+# define RBIMPL_WARNING_POP() __pragma(warning(pop))
+# define RBIMPL_WARNING_ERROR(flag) __pragma(warning(error: flag))
+# define RBIMPL_WARNING_IGNORED(flag) __pragma(warning(disable: flag))
+
+#elif RBIMPL_COMPILER_SINCE(Intel, 13, 0, 0)
+# define RBIMPL_WARNING_PUSH() __pragma(warning(push))
+# define RBIMPL_WARNING_POP() __pragma(warning(pop))
+# define RBIMPL_WARNING_ERROR(flag) __pragma(warning(error: flag))
+# define RBIMPL_WARNING_IGNORED(flag) __pragma(warning(disable: flag))
+
+#elif RBIMPL_COMPILER_IS(Clang) || RBIMPL_COMPILER_IS(Apple)
+# /* Not sure exactly when but it seems LLVM 2.6.0 is a version with it. */
+# define RBIMPL_WARNING_PRAGMA0(x) _Pragma(# x)
+# define RBIMPL_WARNING_PRAGMA1(x) RBIMPL_WARNING_PRAGMA0(clang diagnostic x)
+# define RBIMPL_WARNING_PRAGMA2(x, y) RBIMPL_WARNING_PRAGMA1(x # y)
+# define RBIMPL_WARNING_PUSH() RBIMPL_WARNING_PRAGMA1(push)
+# define RBIMPL_WARNING_POP() RBIMPL_WARNING_PRAGMA1(pop)
+# define RBIMPL_WARNING_ERROR(flag) RBIMPL_WARNING_PRAGMA2(error, flag)
+# define RBIMPL_WARNING_IGNORED(flag) RBIMPL_WARNING_PRAGMA2(ignored, flag)
+
+#elif RBIMPL_COMPILER_SINCE(GCC, 4, 6, 0)
+# /* https://gcc.gnu.org/onlinedocs/gcc-4.6.0/gcc/Diagnostic-Pragmas.html */
+# define RBIMPL_WARNING_PRAGMA0(x) _Pragma(# x)
+# define RBIMPL_WARNING_PRAGMA1(x) RBIMPL_WARNING_PRAGMA0(GCC diagnostic x)
+# define RBIMPL_WARNING_PRAGMA2(x, y) RBIMPL_WARNING_PRAGMA1(x # y)
+# define RBIMPL_WARNING_PUSH() RBIMPL_WARNING_PRAGMA1(push)
+# define RBIMPL_WARNING_POP() RBIMPL_WARNING_PRAGMA1(pop)
+# define RBIMPL_WARNING_ERROR(flag) RBIMPL_WARNING_PRAGMA2(error, flag)
+# define RBIMPL_WARNING_IGNORED(flag) RBIMPL_WARNING_PRAGMA2(ignored, flag)
+
+#else
+# /* :FIXME: improve here */
+# define RBIMPL_WARNING_PUSH() /* void */
+# define RBIMPL_WARNING_POP() /* void */
+# define RBIMPL_WARNING_ERROR(flag) /* void */
+# define RBIMPL_WARNING_IGNORED(flag) /* void */
+#endif /* _MSC_VER */
+/** @endcond */
+
+#endif /* RBIMPL_WARNING_PUSH_H */
diff --git a/include/ruby/internal/xmalloc.h b/include/ruby/internal/xmalloc.h
new file mode 100644
index 0000000000..132bc478ce
--- /dev/null
+++ b/include/ruby/internal/xmalloc.h
@@ -0,0 +1,288 @@
+#ifndef RBIMPL_XMALLOC_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RBIMPL_XMALLOC_H
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
+ * implementation details. Don't take them as canon. They could
+ * rapidly appear then vanish. The name (path) of this header file
+ * is also an implementation detail. Do not expect it to persist
+ * at the place it is now. Developers are free to move it anywhere
+ * anytime at will.
+ * @note To ruby-core: remember that this header can be possibly
+ * recursively included from extension libraries written in C++.
+ * Do not expect for instance `__VA_ARGS__` is always available.
+ * We assume C99 for ruby itself but we don't assume languages of
+ * extension libraries. They could be written in C++98.
+ * @brief Declares ::ruby_xmalloc().
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include "ruby/internal/attr/alloc_size.h"
+#include "ruby/internal/attr/nodiscard.h"
+#include "ruby/internal/attr/noexcept.h"
+#include "ruby/internal/attr/restrict.h"
+#include "ruby/internal/attr/returns_nonnull.h"
+#include "ruby/internal/dllexport.h"
+
+/**
+ * @private
+ * @warning Do not touch this macro.
+ * @warning It is an implementation detail.
+ * @warning It was a failure at the first place to let you know about it.
+ * @warning The value of this macro must match for ruby itself and all
+ * extension libraries, otherwise serious memory corruption shall
+ * occur.
+ */
+#ifndef USE_GC_MALLOC_OBJ_INFO_DETAILS
+# define USE_GC_MALLOC_OBJ_INFO_DETAILS 0
+#endif
+
+#define xmalloc ruby_xmalloc /**< @old{ruby_xmalloc} */
+#define xmalloc2 ruby_xmalloc2 /**< @old{ruby_xmalloc2} */
+#define xcalloc ruby_xcalloc /**< @old{ruby_xcalloc} */
+#define xrealloc ruby_xrealloc /**< @old{ruby_xrealloc} */
+#define xrealloc2 ruby_xrealloc2 /**< @old{ruby_xrealloc2} */
+#define xfree ruby_xfree /**< @old{ruby_xfree} */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+RBIMPL_ATTR_NODISCARD()
+RBIMPL_ATTR_RESTRICT()
+RBIMPL_ATTR_RETURNS_NONNULL()
+RBIMPL_ATTR_ALLOC_SIZE((1))
+/**
+ * Allocates a storage instance. It is largely the same as system malloc(),
+ * except:
+ *
+ * - It raises Ruby exceptions instead of returning NULL, and
+ * - In case of `ENOMEM` it tries to GC to make some room.
+ *
+ * @param[in] size Requested amount of memory.
+ * @exception rb_eNoMemError No space left for `size` bytes allocation.
+ * @return A valid pointer to an allocated storage instance; which has at
+ * least `size` bytes width, with appropriate alignment detected by
+ * the underlying malloc() routine.
+ * @note It doesn't return NULL.
+ * @note Unlike some malloc() implementations, it allocates something and
+ * returns a meaningful value even when `size` is equal to zero.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+void *ruby_xmalloc(size_t size)
+RBIMPL_ATTR_NOEXCEPT(malloc(size))
+;
+
+RBIMPL_ATTR_NODISCARD()
+RBIMPL_ATTR_RESTRICT()
+RBIMPL_ATTR_RETURNS_NONNULL()
+RBIMPL_ATTR_ALLOC_SIZE((1,2))
+/**
+ * Identical to ruby_xmalloc(), except it allocates `nelems` * `elemsiz` bytes.
+ * This is needed because the multiplication could integer overflow. On such
+ * situations Ruby does not try to allocate at all but raises Ruby level
+ * exceptions instead. If there is no integer overflow the behaviour is
+ * exactly the same as `ruby_xmalloc(nelems*elemsiz)`.
+ *
+ * @param[in] nelems Number of elements.
+ * @param[in] elemsiz Size of an element.
+ * @exception rb_eNoMemError No space left for allocation.
+ * @exception rb_eArgError `nelems` * `elemsiz` would overflow.
+ * @return A valid pointer to an allocated storage instance; which has at
+ * least `nelems` * `elemsiz` bytes width, with appropriate
+ * alignment detected by the underlying malloc() routine.
+ * @note It doesn't return NULL.
+ * @note Unlike some malloc() implementations, it allocates something and
+ * returns a meaningful value even when `nelems` or `elemsiz` or
+ * both are zero.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+void *ruby_xmalloc2(size_t nelems, size_t elemsiz)
+RBIMPL_ATTR_NOEXCEPT(malloc(nelems * elemsiz))
+;
+
+RBIMPL_ATTR_NODISCARD()
+RBIMPL_ATTR_RESTRICT()
+RBIMPL_ATTR_RETURNS_NONNULL()
+RBIMPL_ATTR_ALLOC_SIZE((1,2))
+/**
+ * Identical to ruby_xmalloc2(), except it returns a zero-filled storage
+ * instance. It can also be seen as a routine identical to ruby_xmalloc(),
+ * except it calls calloc() instead of malloc().
+ *
+ * @param[in] nelems Number of elements.
+ * @param[in] elemsiz Size of an element.
+ * @exception rb_eNoMemError No space left for allocation.
+ * @exception rb_eArgError `nelems` * `elemsiz` would overflow.
+ * @return A valid pointer to an allocated storage instance; which has at
+ * least `nelems` * `elemsiz` bytes width, with appropriate
+ * alignment detected by the underlying calloc() routine.
+ * @post The returned storage instance is filled with zeros.
+ * @note It doesn't return NULL.
+ * @note Unlike some calloc() implementations, it allocates something and
+ * returns a meaningful value even when `nelems` or `elemsiz` or
+ * both are zero.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+void *ruby_xcalloc(size_t nelems, size_t elemsiz)
+RBIMPL_ATTR_NOEXCEPT(calloc(nelems, elemsiz))
+;
+
+RBIMPL_ATTR_NODISCARD()
+RBIMPL_ATTR_RETURNS_NONNULL()
+RBIMPL_ATTR_ALLOC_SIZE((2))
+/**
+ * Resize the storage instance.
+ *
+ * @param[in] ptr A valid pointer to a storage instance that was
+ * previously returned from either:
+ * - ruby_xmalloc(),
+ * - ruby_xmalloc2(),
+ * - ruby_xcalloc(),
+ * - ruby_xrealloc(), or
+ * - ruby_xrealloc2().
+ * @param[in] newsiz Requested new amount of memory.
+ * @exception rb_eNoMemError No space left for `newsiz` bytes allocation.
+ * @return A valid pointer to a (possibly newly allocated) storage
+ * instance; which has at least `newsiz` bytes width, with
+ * appropriate alignment detected by the underlying realloc()
+ * routine.
+ * @pre The passed pointer must point to a valid live storage instance.
+ * It is a failure to pass an already freed pointer.
+ * @post In case the function returns the passed pointer as-is, the
+ * storage instance that the pointer holds is either grown or
+ * shrunken to have at least `newsiz` bytes. Otherwise a valid
+ * pointer to a newly allocated storage instance is returned. In
+ * this case `ptr` is invalidated as if it was passed to
+ * ruby_xfree().
+ * @note It doesn't return NULL.
+ * @warning Unlike some realloc() implementations, passing zero to `newsiz`
+ * is not the same as calling ruby_xfree(), because this function
+ * never returns NULL. Something meaningful still returns then.
+ * @warning It is a failure not to check the return value. Do not assume
+ * anything on it. It could be either identical to, or distinct
+ * form the passed argument.
+ * @warning Do not assume anything on the alignment of the return value.
+ * There is no guarantee that it inherits the passed argument's
+ * one.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+void *ruby_xrealloc(void *ptr, size_t newsiz)
+RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newsiz))
+;
+
+RBIMPL_ATTR_NODISCARD()
+RBIMPL_ATTR_RETURNS_NONNULL()
+RBIMPL_ATTR_ALLOC_SIZE((2,3))
+/**
+ * Identical to ruby_xrealloc(), except it resizes the given storage instance
+ * to `newelems` * `newsiz` bytes. This is needed because the multiplication
+ * could integer overflow. On such situations Ruby does not try to touch the
+ * contents of argument pointer at all but raises Ruby level exceptions
+ * instead. If there is no integer overflow the behaviour is exactly the same
+ * as `ruby_xrealloc(ptr,nelems*elemsiz)`.
+ *
+ * This is roughly the same as reallocarray() function that OpenBSD
+ * etc. provides, but also interacts with our GC.
+ *
+ * @param[in] ptr A valid pointer to a storage instance that was
+ * previously returned from either:
+ * - ruby_xmalloc(),
+ * - ruby_xmalloc2(),
+ * - ruby_xcalloc(),
+ * - ruby_xrealloc(), or
+ * - ruby_xrealloc2().
+ * @param[in] newelems Requested new number of elements.
+ * @param[in] newsiz Requested new size of each element.
+ * @exception rb_eNoMemError No space left for allocation.
+ * @exception rb_eArgError `newelems` * `newsiz` would overflow.
+ * @return A valid pointer to a (possibly newly allocated) storage
+ * instance; which has at least `newelems` * `newsiz` bytes width,
+ * with appropriate alignment detected by the underlying realloc()
+ * routine.
+ * @pre The passed pointer must point to a valid live storage instance.
+ * It is a failure to pass an already freed pointer.
+ * @post In case the function returns the passed pointer as-is, the
+ * storage instance that the pointer holds is either grown or
+ * shrunken to have at least `newelems` * `newsiz` bytes.
+ * Otherwise a valid pointer to a newly allocated storage instance
+ * is returned. In this case `ptr` is invalidated as if it was
+ * passed to ruby_xfree().
+ * @note It doesn't return NULL.
+ * @warning Unlike some realloc() implementations, passing zero to either
+ * `newelems` or `elemsiz` are not the same as calling
+ * ruby_xfree(), because this function never returns NULL.
+ * Something meaningful still returns then.
+ * @warning It is a failure not to check the return value. Do not assume
+ * anything on it. It could be either identical to, or distinct
+ * form the passed argument.
+ * @warning Do not assume anything on the alignment of the return value.
+ * There is no guarantee that it inherits the passed argument's
+ * one.
+ * @warning The return value shall be invalidated exactly once by either
+ * ruby_xfree(), ruby_xrealloc(), or ruby_xrealloc2(). It is a
+ * failure to pass it to system free(), because the system and Ruby
+ * might or might not share the same malloc() implementation.
+ */
+void *ruby_xrealloc2(void *ptr, size_t newelems, size_t newsiz)
+RBIMPL_ATTR_NOEXCEPT(realloc(ptr, newelems * newsiz))
+;
+
+/**
+ * Deallocates a storage instance.
+ *
+ * @param[out] ptr Either
+ * - NULL, or
+ * - a valid pointer previously returned from one of:
+ * - ruby_xmalloc(),
+ * - ruby_xmalloc2(),
+ * - ruby_xcalloc(),
+ * - ruby_xrealloc(), or
+ * - ruby_xrealloc2().
+ * @pre The passed pointer must point to a valid live storage instance.
+ * It is a failure to pass an already freed pointer.
+ * @post The storage instance pointed by the passed pointer gets
+ * invalidated; it is no longer addressable.
+ * @warning Every single storage instance that was previously allocated by
+ * either ruby_xmalloc(), ruby_xmalloc2(), ruby_xcalloc(),
+ * ruby_xrealloc(), or ruby_xrealloc2() shall be invalidated
+ * exactly once by either passing it to ruby_xfree(), or passing
+ * it to either ruby_xrealloc(), ruby_xrealloc2() then check the
+ * return value for invalidation.
+ * @warning Do not pass anything other than pointers described above. For
+ * instance pointers returned from malloc() or mmap() shall not be
+ * passed to this function, because the underlying memory
+ * management mechanism could differ.
+ * @warning Do not pass any invalid pointers to this function e.g. by
+ * calling it twice with a same argument.
+ */
+void ruby_xfree(void *ptr)
+RBIMPL_ATTR_NOEXCEPT(free(ptr))
+;
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RBIMPL_XMALLOC_H */
diff --git a/include/ruby/io.h b/include/ruby/io.h
new file mode 100644
index 0000000000..ed0967abad
--- /dev/null
+++ b/include/ruby/io.h
@@ -0,0 +1,1132 @@
+#ifndef RUBY_IO_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_IO_H 1
+/**
+ * @file
+ * @author $Author$
+ * @date Fri Nov 12 16:47:09 JST 1993
+ * @copyright Copyright (C) 1993-2007 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#include "ruby/internal/config.h"
+
+#include <stdio.h>
+#include "ruby/encoding.h"
+
+#if defined(HAVE_STDIO_EXT_H)
+#include <stdio_ext.h>
+#endif
+
+#include <errno.h>
+
+/** @cond INTERNAL_MACRO */
+#if defined(HAVE_POLL)
+# ifdef _AIX
+# define reqevents events
+# define rtnevents revents
+# endif
+# include <poll.h>
+# ifdef _AIX
+# undef reqevents
+# undef rtnevents
+# undef events
+# undef revents
+# endif
+# define RB_WAITFD_IN POLLIN
+# if defined(POLLPRI)
+# define RB_WAITFD_PRI POLLPRI
+# else
+# define RB_WAITFD_PRI 0
+# endif
+# define RB_WAITFD_OUT POLLOUT
+#else
+# define RB_WAITFD_IN 0x001
+# define RB_WAITFD_PRI 0x002
+# define RB_WAITFD_OUT 0x004
+#endif
+/** @endcond */
+
+#include "ruby/internal/attr/const.h"
+#include "ruby/internal/attr/packed_struct.h"
+#include "ruby/internal/attr/pure.h"
+#include "ruby/internal/attr/noreturn.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/value.h"
+
+// IO#wait, IO#wait_readable, IO#wait_writable, IO#wait_priority are defined by this implementation.
+#define RUBY_IO_WAIT_METHODS
+
+// Used as the default timeout argument to `rb_io_wait` to use the `IO#timeout` value.
+#define RUBY_IO_TIMEOUT_DEFAULT Qnil
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+struct stat;
+struct timeval;
+
+#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
+# define RUBY_USE_STATX 0
+#elif defined(HAVE_STRUCT_STATX_STX_BTIME)
+# define RUBY_USE_STATX 1
+struct statx;
+#else
+# define RUBY_USE_STATX 0
+#endif
+
+#if RUBY_USE_STATX
+typedef struct statx rb_io_stat_data;
+#else
+typedef struct stat rb_io_stat_data;
+#endif
+
+/**
+ * Indicates that a timeout has occurred while performing an IO operation.
+ */
+RUBY_EXTERN VALUE rb_eIOTimeoutError;
+
+/**
+ * Type of events that an IO can wait.
+ *
+ * @internal
+ *
+ * This is visible from extension libraries because `io/wait` wants it.
+ */
+enum rb_io_event {
+ RUBY_IO_READABLE = RB_WAITFD_IN, /**< `IO::READABLE` */
+ RUBY_IO_WRITABLE = RB_WAITFD_OUT, /**< `IO::WRITABLE` */
+ RUBY_IO_PRIORITY = RB_WAITFD_PRI, /**< `IO::PRIORITY` */
+};
+
+typedef enum rb_io_event rb_io_event_t;
+
+/**
+ * IO buffers. This is an implementation detail of ::rb_io_t::wbuf and
+ * ::rb_io_t::rbuf. People don't manipulate it directly.
+ */
+RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN()
+struct rb_io_internal_buffer {
+
+ /** Pointer to the underlying memory region, of at least `capa` bytes. */
+ char *ptr; /* off + len <= capa */
+
+ /** Offset inside of `ptr`. */
+ int off;
+
+ /** Length of the buffer. */
+ int len;
+
+ /** Designed capacity of the buffer. */
+ int capa;
+} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END();
+
+/** @alias{rb_io_buffer_t} */
+typedef struct rb_io_internal_buffer rb_io_buffer_t;
+
+/** Decomposed encoding flags (e.g. `"enc:enc2""`). */
+/*
+ * enc enc2 read action write action
+ * NULL NULL force_encoding(default_external) write the byte sequence of str
+ * e1 NULL force_encoding(e1) convert str.encoding to e1
+ * e1 e2 convert from e2 to e1 convert str.encoding to e2
+ */
+struct rb_io_encoding {
+ /** Internal encoding. */
+ rb_encoding *enc;
+ /** External encoding. */
+ rb_encoding *enc2;
+ /**
+ * Flags.
+ *
+ * @see enum ::ruby_econv_flag_type
+ */
+ int ecflags;
+ /**
+ * Flags as Ruby hash.
+ *
+ * @internal
+ *
+ * This is set. But used from nowhere maybe?
+ */
+ VALUE ecopts;
+};
+
+/**
+ * @name Possible flags for ::rb_io_t::mode
+ *
+ * @{
+ */
+
+/** The IO is opened for reading. */
+#define FMODE_READABLE 0x00000001
+
+/** The IO is opened for writing. */
+#define FMODE_WRITABLE 0x00000002
+
+/** The IO is opened for both read/write. */
+#define FMODE_READWRITE (FMODE_READABLE|FMODE_WRITABLE)
+
+/**
+ * The IO is in "binary mode". This is not what everything rb_io_binmode()
+ * concerns. This low-level flag is to stop CR <-> CRLF conversions that would
+ * happen in the underlying operating system.
+ *
+ * Setting this one and #FMODE_TEXTMODE at the same time is a contradiction.
+ * Setting this one and #ECONV_NEWLINE_DECORATOR_MASK at the same time is also
+ * a contradiction.
+ */
+#define FMODE_BINMODE 0x00000004
+
+/**
+ * The IO is in "sync mode". All output is immediately flushed to the
+ * underlying operating system then. Can be set via rb_io_synchronized(), but
+ * there is no way except calling `IO#sync=` to reset.
+ */
+#define FMODE_SYNC 0x00000008
+
+/**
+ * The IO is a TTY. What is a TTY and what isn't depends on the underlying
+ * operating system's `isatty(3)` output. You cannot change this.
+ */
+#define FMODE_TTY 0x00000010
+
+/**
+ * Ruby eventually detects that the IO is bidirectional. For instance a TTY
+ * has such property. There are several other things known to be duplexed.
+ * Additionally you (extension library authors) can also implement your own
+ * bidirectional IO subclasses. One of such example is `Socket`.
+ */
+#define FMODE_DUPLEX 0x00000020
+
+/**
+ * The IO is opened for appending. This mode always writes at the end of the
+ * IO. Ruby manages this flag for record but basically the logic behind this
+ * mode is at the underlying operating system. We almost do nothing.
+ */
+#define FMODE_APPEND 0x00000040
+
+/**
+ * The IO is opened for creating. This makes sense only when the destination
+ * file does not exist at the time the IO object was created. This is the
+ * default mode for writing, but you can pass `"r+"` to `IO.open` etc., to
+ * reroute this creation.
+ */
+#define FMODE_CREATE 0x00000080
+/* #define FMODE_NOREVLOOKUP 0x00000100 */
+
+/**
+ * This flag amends the effect of #FMODE_CREATE, so that if there already is a
+ * file at the given path the operation fails. Using this you can be sure that
+ * the file you get is a fresh new one.
+ */
+#define FMODE_EXCL 0x00000400
+
+/**
+ * This flag amends the effect of #FMODE_CREATE, so that if there already is a
+ * file at the given path it gets truncated.
+ */
+#define FMODE_TRUNC 0x00000800
+
+/**
+ * The IO is in "text mode". On systems where such mode make sense, this flag
+ * changes the way the IO handles the contents. On POSIX systems it is
+ * basically a no-op, but with this flag set you can optionally let Ruby
+ * manually convert newlines, unlike when in binary mode:
+ *
+ * ```ruby
+ * IO.open("/p/a/t/h", "wt", crlf_newline: true) # "wb" is NG.
+ * ```
+ *
+ * Setting this one and #FMODE_BINMODE at the same time is a contradiction.
+ */
+#define FMODE_TEXTMODE 0x00001000
+/**
+ * This flag means that an IO object is wrapping an "external" file descriptor,
+ * which is owned by something outside the Ruby interpreter (usually a C extension).
+ * Ruby will not close this file when the IO object is garbage collected.
+ * If this flag is set, then IO#autoclose? is false, and vice-versa.
+ *
+ * This flag was previously called FMODE_PREP internally.
+ */
+#define FMODE_EXTERNAL 0x00010000
+
+/* #define FMODE_SIGNAL_ON_EPIPE 0x00020000 */
+
+/**
+ * This flag amends the encoding of the IO so that the BOM of the contents of
+ * the IO takes effect.
+ */
+#define FMODE_SETENC_BY_BOM 0x00100000
+/* #define FMODE_UNIX 0x00200000 */
+/* #define FMODE_INET 0x00400000 */
+/* #define FMODE_INET6 0x00800000 */
+
+/** @} */
+
+enum rb_io_mode {
+ RUBY_IO_MODE_EXTERNAL = FMODE_EXTERNAL,
+
+ RUBY_IO_MODE_READABLE = FMODE_READABLE,
+ RUBY_IO_MODE_WRITABLE = FMODE_WRITABLE,
+ RUBY_IO_MODE_READABLE_WRITABLE = (RUBY_IO_MODE_READABLE|RUBY_IO_MODE_WRITABLE),
+
+ RUBY_IO_MODE_BINARY = FMODE_BINMODE,
+ RUBY_IO_MODE_TEXT = FMODE_TEXTMODE,
+ RUBY_IO_MODE_TEXT_SET_ENCODING_FROM_BOM = FMODE_SETENC_BY_BOM,
+
+ RUBY_IO_MODE_SYNCHRONISED = FMODE_SYNC,
+
+ RUBY_IO_MODE_TTY = FMODE_TTY,
+
+ RUBY_IO_MODE_DUPLEX = FMODE_DUPLEX,
+
+ RUBY_IO_MODE_APPEND = FMODE_APPEND,
+ RUBY_IO_MODE_CREATE = FMODE_CREATE,
+ RUBY_IO_MODE_EXCLUSIVE = FMODE_EXCL,
+ RUBY_IO_MODE_TRUNCATE = FMODE_TRUNC,
+};
+
+typedef enum rb_io_mode rb_io_mode_t;
+
+#ifndef HAVE_RB_IO_T
+#define HAVE_RB_IO_T 1
+/** Ruby's IO, metadata and buffers. */
+struct rb_io {
+ /** The IO's Ruby level counterpart. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ VALUE self;
+
+ /** stdio ptr for read/write, if available. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ FILE *stdio_file;
+
+ /** file descriptor. */
+ RBIMPL_ATTR_DEPRECATED(("rb_io_descriptor"))
+ int fd;
+
+ /** mode flags: FMODE_XXXs */
+ RBIMPL_ATTR_DEPRECATED(("rb_io_mode"))
+ enum rb_io_mode mode;
+
+ /** child's pid (for pipes) */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ rb_pid_t pid;
+
+ /** number of lines read */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ int lineno;
+
+ /** pathname for file */
+ RBIMPL_ATTR_DEPRECATED(("rb_io_path"))
+ VALUE pathv;
+
+ /** finalize proc */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ void (*finalize)(struct rb_io*,int);
+
+ /** Write buffer. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ rb_io_buffer_t wbuf;
+
+ /**
+ * (Byte) read buffer. Note also that there is a field called
+ * ::rb_io_t::cbuf, which also concerns read IO.
+ */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ rb_io_buffer_t rbuf;
+
+ /**
+ * Duplex IO object, if set.
+ *
+ * @see rb_io_set_write_io()
+ */
+ RBIMPL_ATTR_DEPRECATED(("rb_io_get_write_io"))
+ VALUE tied_io_for_writing;
+
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ struct rb_io_encoding encs; /**< Decomposed encoding flags. */
+
+ /** Encoding converter used when reading from this IO. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ rb_econv_t *readconv;
+
+ /**
+ * rb_io_ungetc() destination. This buffer is read before checking
+ * ::rb_io_t::rbuf
+ */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ rb_io_buffer_t cbuf;
+
+ /** Encoding converter used when writing to this IO. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ rb_econv_t *writeconv;
+
+ /**
+ * This is, when set, an instance of ::rb_cString which holds the "common"
+ * encoding. Write conversion can convert strings twice... In case
+ * conversion from encoding X to encoding Y does not exist, Ruby finds an
+ * encoding Z that bridges the two, so that X to Z to Y conversion happens.
+ */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ VALUE writeconv_asciicompat;
+
+ /** Whether ::rb_io_t::writeconv is already set up. */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ int writeconv_initialized;
+
+ /**
+ * Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before
+ * initialising ::rb_io_t::writeconv.
+ */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ int writeconv_pre_ecflags;
+
+ /**
+ * Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising
+ * ::rb_io_t::writeconv.
+ */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ VALUE writeconv_pre_ecopts;
+
+ /**
+ * This is a Ruby level mutex. It avoids multiple threads to write to an
+ * IO at once; helps for instance rb_io_puts() to ensure newlines right
+ * next to its arguments.
+ *
+ * This of course doesn't help inter-process IO interleaves, though.
+ */
+ RBIMPL_ATTR_DEPRECATED(("with no replacement"))
+ VALUE write_lock;
+
+ /**
+ * The timeout associated with this IO when performing blocking operations.
+ */
+ RBIMPL_ATTR_DEPRECATED(("rb_io_timeout/rb_io_set_timeout"))
+ VALUE timeout;
+};
+#endif
+
+typedef struct rb_io rb_io_t;
+
+/** @alias{rb_io_enc_t} */
+typedef struct rb_io_encoding rb_io_enc_t;
+
+/**
+ * Allocate a new IO object, with the given file descriptor.
+ */
+VALUE rb_io_open_descriptor(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, struct rb_io_encoding *encoding);
+
+/**
+ * Returns whether or not the underlying IO is closed.
+ *
+ * @return Whether the underlying IO is closed.
+ */
+VALUE rb_io_closed_p(VALUE io);
+
+/**
+ * Queries the underlying IO pointer.
+ *
+ * @param[in] obj An IO object.
+ * @param[out] fp A variable of type ::rb_io_t.
+ * @exception rb_eFrozenError `obj` is frozen.
+ * @exception rb_eIOError `obj` is closed.
+ * @post `fp` holds `obj`'s underlying IO.
+ */
+#define RB_IO_POINTER(obj,fp) rb_io_check_closed((fp) = RFILE(rb_io_taint_check(obj))->fptr)
+
+/**
+ * This is an old name of #RB_IO_POINTER. Not sure if we want to deprecate
+ * this macro. There still are tons of usages out there in the wild.
+ */
+#define GetOpenFile RB_IO_POINTER
+
+/**
+ * Fills an IO object. This makes the best sense when called from inside of an
+ * `#initialize` method of a 3rd party extension library that inherits
+ * ::rb_cIO.
+ *
+ * If the passed IO is already opened for something it first closes that and
+ * opens a new one instead.
+ *
+ * @param[out] obj An IO object to fill in.
+ * @param[out] fp A variable of type ::rb_io_t.
+ * @exception rb_eTypeError `obj` is not ::RUBY_T_FILE.
+ * @post `fp` holds `obj`'s underlying IO.
+ */
+#define RB_IO_OPEN(obj, fp) do {\
+ (fp) = rb_io_make_open_file(obj);\
+} while (0)
+
+/**
+ * This is an old name of #RB_IO_OPEN. Not sure if we want to deprecate this
+ * macro. There still are usages out there in the wild.
+ */
+#define MakeOpenFile RB_IO_OPEN
+
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_IO_OPEN. People don't use it
+ * directly.
+ *
+ * @param[out] obj An IO object to fill in.
+ * @exception rb_eTypeError `obj` is not ::RUBY_T_FILE.
+ * @return `obj`'s backend IO.
+ * @post `obj` is initialised.
+ */
+rb_io_t *rb_io_make_open_file(VALUE obj);
+
+/**
+ * Finds or creates a stdio's file structure from a Ruby's one. This can be
+ * handy if you want to call an external API that accepts `FILE *`.
+ *
+ * @note Note however, that `FILE`s can have their own buffer. Mixing Ruby's
+ * and stdio's file are basically dangerous. Use with care.
+ *
+ * @param[in,out] fptr Target IO.
+ * @return A stdio's file, created if absent.
+ * @post `fptr` has its corresponding stdio's file.
+ *
+ * @internal
+ *
+ * We had rich support for `FILE` before! In the days of 1.8.x ::rb_io_t was
+ * like this:
+ *
+ * ```CXX
+ * typedef struct rb_io {
+ * FILE *f; // stdio ptr for read/write
+ * FILE *f2; // additional ptr for rw pipes
+ * int mode; // mode flags
+ * int pid; // child's pid (for pipes)
+ * int lineno; // number of lines read
+ * char *path; // pathname for file
+ * void (*finalize) _((struct rb_io*,int)); // finalize proc
+ * } rb_io_t;
+ *```
+ *
+ * But we eventually abandoned this layout. It was too difficult. We could
+ * not have fine-grained control over the `f` field.
+ *
+ * - `FILE` tends to be an opaque struct. It does not interface well with
+ * `select(2)` etc. This makes IO multiplexing quite hard. Using stdio,
+ * there is arguably no portable way to know if `fwrite(3)` blocks.
+ *
+ * - Nonblocking mode, which is another core concept that enables IO
+ * multiplexing, does not interface with stdio routines at all.
+ *
+ * - Detection of duplexed IO is also hard for the same reason.
+ *
+ * - `feof(3)` is not portable.
+ * https://mail.python.org/pipermail/python-dev/2001-January/011390.html
+ *
+ * - Solaris was a thing back then. They could not have more than 256 `FILE`
+ * structures at a time. Their file descriptors ware stored in an
+ * `unsigned char`.
+ *
+ * - It is next to impossible to avoid SEGV, especially when a thread tries to
+ * `ungetc(3)`-ing from a `FILE` which is `fread(3)`-ed by another one.
+ *
+ * In short, it is a bad idea to let someone else manage IO buffers, especially
+ * someone you cannot control. This still applies to extension libraries
+ * methinks. Ruby doesn't prevent you from shooting yourself in the foot, but
+ * consider yourself warned here.
+ */
+FILE *rb_io_stdio_file(rb_io_t *fptr);
+
+/**
+ * Identical to rb_io_stdio_file(), except it takes file descriptors instead of
+ * Ruby's IO. It can also be seen as a compatibility layer to wrap
+ * `fdopen(3)`. Nowadays all supporting systems, including Windows, have
+ * `fdopen`. Why not use them.
+ *
+ * @param[in] fd A file descriptor.
+ * @param[in] modestr C string, something like `"r+"`.
+ * @exception rb_eSystemCallError `fdopen` failed for some reason.
+ * @return A stdio's file associated with `fd`.
+ * @note Interpretation of `modestr` depends on the underlying operating
+ * system. On glibc you might be able to pass e.g. `"rm"`, but
+ * that's an extension to POSIX.
+ */
+FILE *rb_fdopen(int fd, const char *modestr);
+
+/**
+ * Maps a file mode string (that rb_file_open() takes) into a mixture of
+ * `FMODE_` flags. This for instance returns
+ * `FMODE_WRITABLE | FMODE_TRUNC | FMODE_CREATE | FMODE_EXCL` for `"wx"`.
+ *
+ * @note You cannot pass this return value to OS provided `open(2)` etc.
+ *
+ * @param[in] modestr File mode, in C's string.
+ * @exception rb_eArgError `modestr` is broken.
+ * @return A set of flags.
+ *
+ * @internal
+ *
+ * rb_io_modestr_fmode() is not a pure function because it raises.
+ */
+enum rb_io_mode rb_io_modestr_fmode(const char *modestr);
+
+/**
+ * Identical to rb_io_modestr_fmode(), except it returns a mixture of `O_`
+ * flags. This for instance returns `O_WRONLY | O_TRUNC | O_CREAT | O_EXCL` for
+ * `"wx"`.
+ *
+ * @param[in] modestr File mode, in C's string.
+ * @exception rb_eArgError `modestr` is broken.
+ * @return A set of flags.
+ *
+ * @internal
+ *
+ * rb_io_modestr_oflags() is not a pure function because it raises.
+ */
+int rb_io_modestr_oflags(const char *modestr);
+
+RBIMPL_ATTR_CONST()
+/**
+ * Converts an oflags (that rb_io_modestr_oflags() returns) to a fmode (that
+ * rb_io_mode_flags() returns). This is a purely functional operation.
+ *
+ * @param[in] oflags A set of `O_` flags.
+ * @return Corresponding set of `FMODE_` flags.
+ */
+int rb_io_oflags_fmode(int oflags);
+
+/**
+ * Asserts that an IO is opened for writing.
+ *
+ * @param[in] fptr An IO you want to write to.
+ * @exception rb_eIOError `fptr` is not for writing.
+ * @post Upon successful return `fptr` is ready for writing.
+ *
+ * @internal
+ *
+ * The parameter must have been `const rb_io_t *`.
+ */
+void rb_io_check_writable(rb_io_t *fptr);
+
+/** @alias{rb_io_check_byte_readable} */
+void rb_io_check_readable(rb_io_t *fptr);
+
+/**
+ * Asserts that an IO is opened for character-based reading. A character can
+ * be wider than a byte. Because of this we have to buffer reads from
+ * descriptors. This fiction checks if that is possible.
+ *
+ * @param[in] fptr An IO you want to read characters from.
+ * @exception rb_eIOError `fptr` is not for reading.
+ * @post Upon successful return `fptr` is ready for reading characters.
+ *
+ * @internal
+ *
+ * Unlike rb_io_check_writable() the parameter cannot be `const rb_io_t *`.
+ * Behind the scene this operation flushes its write buffers. This is because
+ * of OpenSSL. They mandate this way.
+ *
+ * @see "Can I use OpenSSL's SSL library with non-blocking I/O?"
+ * https://www.openssl.org/docs/faq.html
+ */
+void rb_io_check_char_readable(rb_io_t *fptr);
+
+/**
+ * Asserts that an IO is opened for byte-based reading. Byte-based and
+ * character-based reading operations cannot be mixed at a time.
+ *
+ * @param[in] fptr An IO you want to read characters from.
+ * @exception rb_eIOError `fptr` is not for reading.
+ * @post Upon successful return `fptr` is ready for reading bytes.
+ */
+void rb_io_check_byte_readable(rb_io_t *fptr);
+
+/**
+ * Destroys the given IO. Any pending operations are flushed.
+ *
+ * @note It makes no sense to call this function from anywhere outside of your
+ * class' ::rb_data_type_struct::dfree.
+ *
+ * @param[out] fptr IO to close.
+ * @post `fptr` is no longer a valid pointer.
+ */
+int rb_io_fptr_finalize(rb_io_t *fptr);
+
+/**
+ * Sets #FMODE_SYNC.
+ *
+ * @note There is no way for C extensions to undo this operation.
+ *
+ * @param[out] fptr IO to set the flag.
+ * @exception rb_eIOError `fptr` is not opened.
+ * @post `fptr` is in sync mode.
+ */
+void rb_io_synchronized(rb_io_t *fptr);
+
+/**
+ * Asserts that the passed IO is initialised.
+ *
+ * @param[in] fptr IO that you expect be initialised.
+ * @exception rb_eIOError `fptr` is not initialised.
+ * @post `fptr` is initialised.
+ */
+void rb_io_check_initialized(rb_io_t *fptr);
+
+/**
+ * This badly named function asserts that the passed IO is _open_.
+ *
+ * @param[in] fptr An IO
+ * @exception rb_eIOError `fptr` is closed.
+ * @post `fptr` is open.
+ */
+void rb_io_check_closed(rb_io_t *fptr);
+
+/**
+ * Identical to rb_io_check_io(), except it raises exceptions on conversion
+ * failures.
+ *
+ * @param[in] io Target object.
+ * @exception rb_eTypeError No implicit conversion to IO.
+ * @return Return value of `obj.to_io`.
+ * @see rb_str_to_str
+ * @see rb_ary_to_ary
+ */
+VALUE rb_io_get_io(VALUE io);
+
+/**
+ * Try converting an object to its IO representation using its `to_io` method,
+ * if any. If there is no such thing, returns ::RUBY_Qnil.
+ *
+ * @param[in] io Arbitrary ruby object to convert.
+ * @exception rb_eTypeError `obj.to_io` returned something non-IO.
+ * @retval RUBY_Qnil No conversion from `obj` to IO defined.
+ * @retval otherwise Converted IO representation of `obj`.
+ * @see rb_check_array_type
+ * @see rb_check_string_type
+ * @see rb_check_hash_type
+ */
+VALUE rb_io_check_io(VALUE io);
+
+/**
+ * Queries the tied IO for writing. An IO can be duplexed. Fine. The thing
+ * is, that characteristics could sometimes be achieved by the underlying
+ * operating system (for instance a socket's duplexity is by nature) but
+ * sometimes by us. Notable example is a bidirectional pipe. Suppose you
+ * have:
+ *
+ * ```ruby
+ * fp = IO.popen("-", "r+")
+ * ```
+ *
+ * This pipe is duplexed (the `"r+"`). You can both read from/write to it.
+ * However your operating system may or may not implement bidirectional pipes.
+ * FreeBSD is one of such operating systems known to have one; OTOH Linux is
+ * known to lack such things. So to achieve maximum portability, Ruby's
+ * bidirectional pipes are done purely in user land. A pipe in ruby can have
+ * multiple file descriptors; one for reading and the other for writing. This
+ * API is to obtain the IO port which corresponds to the passed one, for
+ * writing.
+ *
+ * @param[in] io An IO.
+ * @return Its tied IO for writing, if any, or `io` itself otherwise.
+ */
+VALUE rb_io_get_write_io(VALUE io);
+
+/**
+ * Assigns the tied IO for writing. See rb_io_get_write_io() for what a "tied
+ * IO for writing" is.
+ *
+ * @param[out] io An IO.
+ * @param[in] w Another IO.
+ * @retval RUBY_Qnil There was no tied IO for writing for `io`.
+ * @retval otherwise The IO formerly tied to `io`.
+ * @post `io` ties `w` for writing.
+ *
+ * @internal
+ *
+ * @shyouhei doesn't think there is any needs of this function for 3rd party
+ * extension libraries.
+ */
+VALUE rb_io_set_write_io(VALUE io, VALUE w);
+
+/**
+ * Instructs the OS to put its internal file structure into "nonblocking mode".
+ * This is an in-Kernel concept. Reading from/writing to that file using C
+ * function calls would return -1 with errno set. However when it comes to a
+ * ruby program, we hide that error behind our `IO#read` method. Ruby level
+ * `IO#read` blocks regardless of this flag. If you want to avoid blocking,
+ * you should consider using methods like `IO#readpartial`.
+ *
+ * ```ruby
+ * require 'io/nonblock'
+ * STDIN.nonblock = true
+ * STDIN.gets # blocks.
+ * ```
+ *
+ * As of writing there is a room of this API in Fiber schedulers. A Fiber
+ * scheduler could be written in a way its behaviour depends on this property.
+ * You need an in-depth understanding of how schedulers work to properly
+ * leverage this, though.
+ *
+ * @note Note however that nonblocking-ness propagates across process
+ * boundaries. You must really carefully watch your step when turning
+ * for instance `stderr` into nonblock mode (it tends to be shared
+ * across many processes). Also it is a complete disaster to mix a
+ * nonblocking file and stdio, and `stderr` tends to be under control of
+ * stdio in other processes.
+ *
+ * @param[out] fptr An IO that is to ne nonblocking.
+ * @post Descriptor that `fptr` describes is under nonblocking mode.
+ *
+ * @internal
+ *
+ * There is `O_NONBLOCK` but not `FMODE_NONBLOCK`. You cannot atomically
+ * create a nonblocking file descriptor using our API.
+ */
+void rb_io_set_nonblock(rb_io_t *fptr);
+
+/**
+ * Returns the path for the given IO.
+ *
+ */
+VALUE rb_io_path(VALUE io);
+
+/**
+ * Returns an integer representing the numeric file descriptor for
+ * <em>io</em>.
+ *
+ * @param[in] io An IO.
+ * @retval int A file descriptor.
+ */
+int rb_io_descriptor(VALUE io);
+
+/**
+ * Get the mode of the IO.
+ *
+ */
+int rb_io_mode(VALUE io);
+
+/**
+ * This function breaks down the option hash that `IO#initialize` takes into
+ * components. This is an implementation detail of rb_io_extract_modeenc()
+ * today. People prefer that API instead.
+ *
+ * @param[in] opt The hash to decompose.
+ * @param[out] enc_p Return value buffer.
+ * @param[out] enc2_p Return value buffer.
+ * @param[out] fmode_p Return value buffer.
+ * @exception rb_eTypeError `opt` is broken.
+ * @exception rb_eArgError Specified encoding does not exist.
+ * @retval 1 Components got extracted.
+ * @retval 0 Otherwise.
+ * @post `enc_p` is the specified internal encoding.
+ * @post `enc2_p` is the specified external encoding.
+ * @post `fmode_p` is the specified set of `FMODE_` modes.
+ */
+int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, enum rb_io_mode *fmode_p);
+
+/**
+ * This function can be seen as an extended version of
+ * rb_io_extract_encoding_option() that not only concerns the option hash but
+ * also mode string and so on. This should be mixed with rb_scan_args() like:
+ *
+ * ```CXX
+ * // This method mimics File.new
+ * static VALUE
+ * your_method(int argc, const VALUE *argv, VALUE self)
+ * {
+ * VALUE f; // file name
+ * VALUE m; // open mode
+ * VALUE p; // permission (O_CREAT)
+ * VALUE k; // keywords
+ * rb_io_enc_t c; // converter
+ * int oflags;
+ * int fmode;
+ *
+ * int n = rb_scan_args(argc, argv, "12:", &f, &m, &p, &k);
+ * rb_io_extract_modeenc(&m, &p, k, &oflags, &fmode, &c);
+ *
+ * // Every local variables declared so far has been properly filled here.
+ * ...
+ * }
+ * ```
+ *
+ * @param[in,out] vmode_p Pointer to a mode object.
+ * @param[in,out] vperm_p Pointer to a permission object.
+ * @param[in] opthash Keyword arguments
+ * @param[out] oflags_p `O_` flags return buffer.
+ * @param[out] fmode_p `FMODE_` flags return buffer.
+ * @param[out] convconfig_p Encoding config return buffer.
+ * @exception rb_eTypeError Unexpected object (e.g. Time) passed.
+ * @exception rb_eArgError Contradiction inside of params.
+ * @post `*vmode_p` is a mode object (filled if any).
+ * @post `*vperm_p` is a permission object (filled if any).
+ * @post `*oflags_p` is filled with `O_` flags.
+ * @post `*fmode_p` is filled with `FMODE_` flags.
+ * @post `*convconfig_p` is filled with conversion instructions.
+ *
+ * @internal
+ *
+ * ```rbs
+ * class File
+ * def initialize: (
+ * (String | int) path,
+ * ?(String | int) fmode,
+ * ?(String | int) perm,
+ * ?mode: (String | int),
+ * ?flags: int,
+ * ?external_encoding: (Encoding | String),
+ * ?internal_encoding: (Encoding | String),
+ * ?encoding: String,
+ * ?textmode: bool,
+ * ?binmode: bool,
+ * ?autoclose: bool,
+ * ?invalid: :replace,
+ * ?undef: :replace,
+ * ?replace: String,
+ * ?fallback: (Hash | Proc | Method),
+ * ?xml: (:text | :attr),
+ * ?crlf_newline: bool,
+ * ?cr_newline: bool,
+ * ?universal_newline: bool
+ * ) -> void
+ * ```
+ */
+void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, int *oflags_p, enum rb_io_mode *fmode_p, rb_io_enc_t *convconfig_p);
+
+/* :TODO: can this function be __attribute__((warn_unused_result)) or not? */
+/**
+ * Buffered write to the passed IO.
+ *
+ * @param[out] io Destination IO.
+ * @param[in] buf Contents to go to `io`.
+ * @param[in] size Number of bytes of `buf`.
+ * @exception rb_eFrozenError `io` is frozen.
+ * @exception rb_eIOError `io` is not open for writing.
+ * @exception rb_eSystemCallError `writev(2)` failed for some reason.
+ * @retval -1 Write failed.
+ * @retval otherwise Number of bytes actually written.
+ * @post `buf` is written to `io`.
+ * @note Partial write is a thing. It is a failure not to check the
+ * return value.
+ */
+ssize_t rb_io_bufwrite(VALUE io, const void *buf, size_t size);
+
+//RBIMPL_ATTR_DEPRECATED(("use rb_io_maybe_wait_readable"))
+/**
+ * Blocks until the passed file descriptor gets readable.
+ *
+ * @deprecated We now prefer rb_io_maybe_wait_readable() over this one.
+ * @param[in] fd The file descriptor to wait.
+ * @exception rb_eIOError Bad file descriptor.
+ * @return 0 or 1 (meaning unclear).
+ * @post `fd` is ready for reading.
+ */
+int rb_io_wait_readable(int fd);
+
+//RBIMPL_ATTR_DEPRECATED(("use rb_io_maybe_wait_writable"))
+/**
+ * Blocks until the passed file descriptor gets writable.
+ *
+ * @deprecated We now prefer rb_io_maybe_wait_writable() over this one.
+ * @param[in] fd The file descriptor to wait.
+ * @exception rb_eIOError Bad file descriptor.
+ * @return 0 or 1 (meaning unclear).
+ */
+int rb_io_wait_writable(int fd);
+
+//RBIMPL_ATTR_DEPRECATED(("use rb_io_wait"))
+/**
+ * Blocks until the passed file descriptor is ready for the passed events.
+ *
+ * @deprecated We now prefer rb_io_maybe_wait() over this one.
+ * @param[in] fd The file descriptor to wait.
+ * @param[in] events A set of enum ::rb_io_event_t.
+ * @param[in,out] tv Timeout.
+ * @retval 0 Operation timed out.
+ * @retval -1 `select(2)` failed for some reason.
+ * @retval otherwise A set of enum ::rb_io_event_t.
+ * @note Depending on your operating system `tv` might or might not
+ * be updated (POSIX permits both). Portable programs must
+ * have no assumptions.
+ */
+int rb_wait_for_single_fd(int fd, int events, struct timeval *tv);
+
+/**
+ * Get the timeout associated with the specified io object.
+ *
+ * @param[in] io An IO object.
+ * @retval RUBY_Qnil There is no associated timeout.
+ * @retval Otherwise The timeout value.
+ */
+VALUE rb_io_timeout(VALUE io);
+
+/**
+ * Set the timeout associated with the specified io object. This timeout is
+ * used as a best effort timeout to prevent operations from blocking forever.
+ *
+ * @param[in] io An IO object.
+ * @param[in] timeout A timeout value. Must respond to #to_f.
+ * @
+ */
+VALUE rb_io_set_timeout(VALUE io, VALUE timeout);
+
+/**
+ * Blocks until the passed IO is ready for the passed events. The "events"
+ * here is a Ruby level integer, which is an OR-ed value of `IO::READABLE`,
+ * `IO::WRITable`, and `IO::PRIORITY`.
+ *
+ * If timeout is `Qnil`, it will use the default timeout as given by
+ * `rb_io_timeout(io)`.
+ *
+ * @param[in] io An IO object to wait.
+ * @param[in] events See above.
+ * @param[in] timeout Time, or numeric seconds since UNIX epoch.
+ * If Qnil, use the default timeout. If Qfalse
+ * or Qundef, wait forever.
+ * @exception rb_eIOError `io` is not open.
+ * @exception rb_eRangeError `timeout` is out of range.
+ * @exception rb_eSystemCallError `select(2)` failed for some reason.
+ * @retval RUBY_Qfalse Operation timed out.
+ * @retval Otherwise Actual events reached.
+ */
+VALUE rb_io_wait(VALUE io, VALUE events, VALUE timeout);
+
+/**
+ * Identical to rb_io_wait() except it additionally takes previous errno. If
+ * the passed errno indicates for instance `EINTR`, this function returns
+ * immediately. This is expected to be called in a loop.
+ *
+ * ```CXX
+ * while (true) {
+ *
+ * ... // Your interesting operation here
+ * // `errno` could be updated
+ *
+ * rb_io_maybe_wait(errno, io, ev, Qnil);
+ * }
+ * ```
+ *
+ * On timeout, ::RUBY_Qfalse is returned. Unless you are specifically handling
+ * the timeouts, you should typically raise ::rb_eIOTimeoutError in this case.
+ *
+ * @param[in] error System errno.
+ * @param[in] io An IO object to wait.
+ * @param[in] events An integer set of interests.
+ * @param[in] timeout Time, or numeric seconds since UNIX epoch.
+ * @exception rb_eIOError `io` is not open.
+ * @exception rb_eRangeError `timeout` is out of range.
+ * @exception rb_eSystemCallError `select(2)` failed for some reason.
+ * @retval RUBY_Qfalse Operation timed out.
+ * @retval RUBY_Qnil Operation failed for some other reason (errno).
+ * @retval Otherwise Actual events reached.
+ *
+ */
+VALUE rb_io_maybe_wait(int error, VALUE io, VALUE events, VALUE timeout);
+
+/**
+ * Blocks until the passed IO is ready for reading, if that makes sense for the
+ * passed errno. This is a special case of rb_io_maybe_wait() that is
+ * only concerned with reading and handles the timeout.
+ *
+ * If you do not want the default timeout handling, consider using
+ * ::rb_io_maybe_wait directly.
+ *
+ * @param[in] error System errno.
+ * @param[in] io An IO object to wait.
+ * @param[in] timeout Time, or numeric seconds since UNIX epoch.
+ * @exception rb_eIOError `io` is not open.
+ * @exception rb_eRangeError `timeout` is out of range.
+ * @exception rb_eSystemCallError `select(2)` failed for some reason.
+ * @exception rb_eIOTimeoutError The wait operation timed out.
+ * @retval Otherwise Always returns ::RUBY_IO_READABLE.
+ */
+int rb_io_maybe_wait_readable(int error, VALUE io, VALUE timeout);
+
+/**
+ * Blocks until the passed IO is ready for writing, if that makes sense for the
+ * passed errno. This is a special case of rb_io_maybe_wait() that is
+ * only concerned with writing, and handles the timeout.
+ *
+ * If you do not want the default timeout handling, consider using
+ * ::rb_io_maybe_wait directly.
+ *
+ * @param[in] error System errno.
+ * @param[in] io An IO object to wait.
+ * @param[in] timeout Time, or numeric seconds since UNIX epoch.
+ * @exception rb_eIOError `io` is not open.
+ * @exception rb_eRangeError `timeout` is out of range.
+ * @exception rb_eSystemCallError `select(2)` failed for some reason.
+ * @exception rb_eIOTimeoutError The wait operation timed out.
+ * @retval Otherwise Always returns ::RUBY_IO_WRITABLE.
+ */
+int rb_io_maybe_wait_writable(int error, VALUE io, VALUE timeout);
+
+/** @cond INTERNAL_MACRO */
+/* compatibility for ruby 1.8 and older */
+#define rb_io_mode_flags(modestr) [<"rb_io_mode_flags() is obsolete; use rb_io_modestr_fmode()">]
+#define rb_io_modenum_flags(oflags) [<"rb_io_modenum_flags() is obsolete; use rb_io_oflags_fmode()">]
+/** @endcond */
+
+/**
+ * @deprecated This function once was a thing in the old days, but makes no
+ * sense any longer today. Exists here for backwards
+ * compatibility only. You can safely forget about it.
+ *
+ * @param[in] obj Object in question.
+ * @exception rb_eFrozenError obj is frozen.
+ * @return The passed `obj`
+ */
+VALUE rb_io_taint_check(VALUE obj);
+
+RBIMPL_ATTR_NORETURN()
+/**
+ * Utility function to raise ::rb_eEOFError.
+ *
+ * @exception rb_eEOFError End of file situation.
+ * @note It never returns.
+ */
+void rb_eof_error(void);
+
+/**
+ * Blocks until there is a pending read in the passed IO. If there already is
+ * it just returns.
+ *
+ * @param[out] fptr An IO to wait for reading.
+ * @post The are bytes to be read.
+ */
+void rb_io_read_check(rb_io_t *fptr);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Queries if the passed IO has any pending reads. Unlike rb_io_read_check()
+ * this doesn't block; has no side effects.
+ *
+ * @param[in] fptr An IO which can have pending reads.
+ * @retval 0 The IO is empty.
+ * @retval 1 There is something buffered.
+ */
+int rb_io_read_pending(rb_io_t *fptr);
+
+/**
+ * Constructs an instance of ::rb_cStat from the passed information.
+ *
+ * @param[in] st A stat.
+ * @return Allocated new instance of ::rb_cStat.
+ */
+VALUE rb_stat_new(const struct stat *st);
+
+#if RUBY_USE_STATX
+/**
+ * Constructs an instance of ::rb_cStat from the passed information.
+ *
+ * @param[in] st A stat.
+ * @return Allocated new instance of ::rb_cStat.
+ */
+VALUE rb_statx_new(const rb_io_stat_data *st);
+#else
+# define rb_statx_new rb_stat_new
+#endif
+
+/* gc.c */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_IO_H */
diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h
new file mode 100644
index 0000000000..e4d98bf051
--- /dev/null
+++ b/include/ruby/io/buffer.h
@@ -0,0 +1,110 @@
+#ifndef RUBY_IO_BUFFER_H
+#define RUBY_IO_BUFFER_H
+/**
+ * @file
+ * @author Samuel Williams
+ * @date Fri 2 Jul 2021 16:29:01 NZST
+ * @copyright Copyright (C) 2021 Samuel Williams
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+
+#pragma once
+
+#include "ruby/ruby.h"
+#include "ruby/internal/config.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+// WARNING: This entire interface is experimental and may change in the future!
+#define RB_IO_BUFFER_EXPERIMENTAL 1
+
+#define RUBY_IO_BUFFER_VERSION 2
+
+// The `IO::Buffer` class.
+RUBY_EXTERN VALUE rb_cIOBuffer;
+
+// The operating system page size.
+RUBY_EXTERN size_t RUBY_IO_BUFFER_PAGE_SIZE;
+
+// The default buffer size, usually a (small) multiple of the page size.
+// Can be overridden by the RUBY_IO_BUFFER_DEFAULT_SIZE environment variable.
+RUBY_EXTERN size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
+
+// Represents the internal state of the buffer.
+// More than one flag can be set at a time.
+enum rb_io_buffer_flags {
+ // The memory in the buffer is owned by someone else.
+ // More specifically, it means that someone else owns the buffer and we shouldn't try to resize it.
+ RB_IO_BUFFER_EXTERNAL = 1,
+ // The memory in the buffer is allocated internally.
+ RB_IO_BUFFER_INTERNAL = 2,
+ // The memory in the buffer is mapped.
+ // A non-private mapping is marked as external.
+ RB_IO_BUFFER_MAPPED = 4,
+
+ // A mapped buffer that is also shared.
+ RB_IO_BUFFER_SHARED = 8,
+
+ // The buffer is locked and cannot be resized.
+ // More specifically, it means we can't change the base address or size.
+ // A buffer is typically locked before a system call that uses the data.
+ RB_IO_BUFFER_LOCKED = 32,
+
+ // The buffer mapping is private and will not impact other processes or the underlying file.
+ RB_IO_BUFFER_PRIVATE = 64,
+
+ // The buffer is read-only and cannot be modified.
+ RB_IO_BUFFER_READONLY = 128,
+
+ // The buffer is backed by a file.
+ RB_IO_BUFFER_FILE = 256,
+};
+
+// Represents the endian of the data types.
+enum rb_io_buffer_endian {
+ // The least significant units are put first.
+ RB_IO_BUFFER_LITTLE_ENDIAN = 4,
+ RB_IO_BUFFER_BIG_ENDIAN = 8,
+
+#if defined(WORDS_BIGENDIAN)
+ RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN,
+#else
+ RB_IO_BUFFER_HOST_ENDIAN = RB_IO_BUFFER_LITTLE_ENDIAN,
+#endif
+
+ RB_IO_BUFFER_NETWORK_ENDIAN = RB_IO_BUFFER_BIG_ENDIAN
+};
+
+VALUE rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags);
+VALUE rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags);
+
+VALUE rb_io_buffer_lock(VALUE self);
+VALUE rb_io_buffer_unlock(VALUE self);
+int rb_io_buffer_try_unlock(VALUE self);
+
+VALUE rb_io_buffer_free(VALUE self);
+VALUE rb_io_buffer_free_locked(VALUE self);
+
+// Access the internal buffer and flags. Validates the pointers.
+// The points may not remain valid if the source buffer is manipulated.
+// Consider using rb_io_buffer_lock if needed.
+enum rb_io_buffer_flags rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size);
+void rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size);
+void rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size);
+
+VALUE rb_io_buffer_transfer(VALUE self);
+void rb_io_buffer_resize(VALUE self, size_t size);
+void rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length);
+
+// The length is the minimum required length.
+VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset);
+VALUE rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset);
+VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset);
+VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_IO_BUFFER_H */
diff --git a/include/ruby/memory_view.h b/include/ruby/memory_view.h
new file mode 100644
index 0000000000..42309d5afc
--- /dev/null
+++ b/include/ruby/memory_view.h
@@ -0,0 +1,325 @@
+#ifndef RUBY_MEMORY_VIEW_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_MEMORY_VIEW_H 1
+/**
+ * @file
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @brief Memory View.
+ */
+
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h> /* size_t */
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h> /* ssize_t */
+#endif
+
+#include "ruby/internal/attr/pure.h" /* RBIMPL_ATTR_PURE */
+#include "ruby/internal/core/rtypeddata.h" /* rb_data_type_t */
+#include "ruby/internal/dllexport.h" /* RUBY_EXTERN */
+#include "ruby/internal/stdbool.h" /* bool */
+#include "ruby/internal/value.h" /* VALUE */
+
+/**
+ * Flags passed to rb_memory_view_get(), then to ::rb_memory_view_get_func_t.
+ */
+enum ruby_memory_view_flags {
+ RUBY_MEMORY_VIEW_SIMPLE = 0,
+ RUBY_MEMORY_VIEW_WRITABLE = (1<<0),
+ RUBY_MEMORY_VIEW_FORMAT = (1<<1),
+ RUBY_MEMORY_VIEW_MULTI_DIMENSIONAL = (1<<2),
+ RUBY_MEMORY_VIEW_STRIDES = (1<<3) | RUBY_MEMORY_VIEW_MULTI_DIMENSIONAL,
+ RUBY_MEMORY_VIEW_ROW_MAJOR = (1<<4) | RUBY_MEMORY_VIEW_STRIDES,
+ RUBY_MEMORY_VIEW_COLUMN_MAJOR = (1<<5) | RUBY_MEMORY_VIEW_STRIDES,
+ RUBY_MEMORY_VIEW_ANY_CONTIGUOUS = RUBY_MEMORY_VIEW_ROW_MAJOR | RUBY_MEMORY_VIEW_COLUMN_MAJOR,
+ RUBY_MEMORY_VIEW_INDIRECT = (1<<6) | RUBY_MEMORY_VIEW_STRIDES,
+};
+
+/** Memory view component metadata. */
+typedef struct {
+ /** @see ::rb_memory_view_t::format */
+ char format;
+
+ /** :FIXME: what is a "native" size is unclear. */
+ bool native_size_p;
+
+ /** Endian of the component */
+ bool little_endian_p;
+
+ /** The component's offset. */
+ size_t offset;
+
+ /** The component's size. */
+ size_t size;
+
+ /**
+ * How many numbers of components are there. For instance "CCC"'s repeat is
+ * 3.
+ */
+ size_t repeat;
+} rb_memory_view_item_component_t;
+
+/**
+ * A MemoryView structure, `rb_memory_view_t`, is used for exporting objects'
+ * MemoryView.
+ *
+ * This structure contains the reference of the object, which is the owner of
+ * the MemoryView, the pointer to the head of exported memory, and the metadata
+ * that describes the structure of the memory. The metadata can describe
+ * multidimensional arrays with strides.
+ */
+typedef struct {
+ /**
+ * The original object that has the memory exported via this memory view.
+ */
+ VALUE obj;
+
+ /** The pointer to the exported memory. */
+ void *data;
+
+ /** The number of bytes in data. */
+ ssize_t byte_size;
+
+ /** true for readonly memory, false for writable memory. */
+ bool readonly;
+
+ /**
+ * A string to describe the format of an element, or NULL for unsigned bytes.
+ * The format string is a sequence of the following pack-template specifiers:
+ *
+ * c, C, s, s!, S, S!, n, v, i, i!, I, I!, l, l!, L, L!,
+ * N, V, f, e, g, q, q!, Q, Q!, d, E, G, j, J, x
+ *
+ * For example, "dd" for an element that consists of two double values,
+ * and "CCC" for an element that consists of three bytes, such as
+ * an RGB color triplet.
+ *
+ * Also, the value endianness can be explicitly specified by '<' or '>'
+ * following a value type specifier.
+ *
+ * The items are packed contiguously. When you emulate the alignment of
+ * structure members, put '|' at the beginning of the format string,
+ * like "|iqc". On x86_64 Linux ABI, the size of the item by this format
+ * is 24 bytes instead of 13 bytes.
+ */
+ const char *format;
+
+ /**
+ * The number of bytes in each element.
+ * item_size should equal to rb_memory_view_item_size_from_format(format). */
+ ssize_t item_size;
+
+ /** Description of each components. */
+ struct {
+ /**
+ * The array of rb_memory_view_item_component_t that describes the
+ * item structure. rb_memory_view_prepare_item_desc and
+ * rb_memory_view_get_item allocate this memory if needed,
+ * and rb_memory_view_release frees it. */
+ const rb_memory_view_item_component_t *components;
+
+ /** The number of components in an item. */
+ size_t length;
+ } item_desc;
+
+ /** The number of dimension. */
+ ssize_t ndim;
+
+ /**
+ * ndim size array indicating the number of elements in each dimension.
+ * This can be NULL when ndim == 1. */
+ const ssize_t *shape;
+
+ /**
+ * ndim size array indicating the number of bytes to skip to go to the
+ * next element in each dimension. */
+ const ssize_t *strides;
+
+ /**
+ * The offset in each dimension when this memory view exposes a nested array.
+ * Or, NULL when this memory view exposes a flat array. */
+ const ssize_t *sub_offsets;
+
+ /** The private data for managing this exported memory */
+ void *private_data;
+
+ /** DO NOT TOUCH THIS: The memory view entry for the internal use */
+ const struct rb_memory_view_entry *_memory_view_entry;
+} rb_memory_view_t;
+
+/** Type of function of ::rb_memory_view_entry_t::get_func. */
+typedef bool (* rb_memory_view_get_func_t)(VALUE obj, rb_memory_view_t *view, int flags);
+
+/** Type of function of ::rb_memory_view_entry_t::release_func. */
+typedef bool (* rb_memory_view_release_func_t)(VALUE obj, rb_memory_view_t *view);
+
+/** Type of function of ::rb_memory_view_entry_t::available_p_func. */
+typedef bool (* rb_memory_view_available_p_func_t)(VALUE obj);
+
+/** Operations applied to a specific kind of a memory view. */
+typedef struct rb_memory_view_entry {
+ /**
+ * Exports a memory view from a Ruby object.
+ */
+ rb_memory_view_get_func_t get_func;
+
+ /**
+ * Releases a memory view that was previously generated using
+ * ::rb_memory_view_entry_t::get_func.
+ */
+ rb_memory_view_release_func_t release_func;
+
+ /**
+ * Queries if an object understands memory view protocol.
+ */
+ rb_memory_view_available_p_func_t available_p_func;
+} rb_memory_view_entry_t;
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* memory_view.c */
+
+/**
+ * Associates the passed class with the passed memory view entry. This has to
+ * be called before actually creating a memory view from an instance.
+ */
+bool rb_memory_view_register(VALUE klass, const rb_memory_view_entry_t *entry);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Return `true` if the data in the MemoryView `view` is row-major contiguous.
+ *
+ * Return `false` otherwise.
+ */
+bool rb_memory_view_is_row_major_contiguous(const rb_memory_view_t *view);
+
+RBIMPL_ATTR_PURE()
+/**
+ * Return `true` if the data in the MemoryView `view` is column-major
+ * contiguous.
+ *
+ * Return `false` otherwise.
+ */
+bool rb_memory_view_is_column_major_contiguous(const rb_memory_view_t *view);
+
+RBIMPL_ATTR_NOALIAS()
+/**
+ * Fill the `strides` array with byte-Strides of a contiguous array of the
+ * given shape with the given element size.
+ */
+void rb_memory_view_fill_contiguous_strides(const ssize_t ndim, const ssize_t item_size, const ssize_t *const shape, const bool row_major_p, ssize_t *const strides);
+
+RBIMPL_ATTR_NOALIAS()
+/**
+ * Fill the members of `view` as an 1-dimensional byte array.
+ */
+bool rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const bool readonly);
+
+/**
+ * Deconstructs the passed format string, as describe in
+ * ::rb_memory_view_t::format.
+ */
+ssize_t rb_memory_view_parse_item_format(const char *format,
+ rb_memory_view_item_component_t **members,
+ size_t *n_members, const char **err);
+
+/**
+ * Calculate the number of bytes occupied by an element.
+ *
+ * When the calculation fails, the failed location in `format` is stored into
+ * `err`, and returns `-1`.
+ */
+ssize_t rb_memory_view_item_size_from_format(const char *format, const char **err);
+
+/**
+ * Calculate the location of the item indicated by the given `indices`.
+ *
+ * The length of `indices` must equal to `view->ndim`.
+ *
+ * This function initializes `view->item_desc` if needed.
+ */
+void *rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices);
+
+/**
+ * Return a value that consists of item members.
+ *
+ * When an item is a single member, the return value is a single value.
+ *
+ * When an item consists of multiple members, an array will be returned.
+ */
+VALUE rb_memory_view_extract_item_members(const void *ptr, const rb_memory_view_item_component_t *members, const size_t n_members);
+
+/** Fill the `item_desc` member of `view`. */
+void rb_memory_view_prepare_item_desc(rb_memory_view_t *view);
+
+/** * Return a value that consists of item members in the given memory view. */
+VALUE rb_memory_view_get_item(rb_memory_view_t *view, const ssize_t *indices);
+
+/**
+ * Return `true` if `obj` supports to export a MemoryView. Return `false`
+ * otherwise.
+ *
+ * If this function returns `true`, it doesn't mean the function
+ * `rb_memory_view_get` will succeed.
+ */
+bool rb_memory_view_available_p(VALUE obj);
+
+/**
+ * If the given `obj` supports to export a MemoryView that conforms the given
+ * `flags`, this function fills `view` by the information of the MemoryView and
+ * returns `true`. In this case, the reference count of `obj` is increased.
+ *
+ * If the given combination of `obj` and `flags` cannot export a MemoryView,
+ * this function returns `false`. The content of `view` is not touched in this
+ * case.
+ *
+ * The exported MemoryView must be released by `rb_memory_view_release` when
+ * the MemoryView is no longer needed.
+ */
+bool rb_memory_view_get(VALUE obj, rb_memory_view_t* memory_view, int flags);
+
+/**
+ * Release the given MemoryView `view` and decrement the reference count of
+ * `memory_view->obj`.
+ *
+ * Consumers must call this function when the MemoryView is no longer needed.
+ * Missing to call this function leads memory leak.
+ */
+bool rb_memory_view_release(rb_memory_view_t* memory_view);
+
+/* for testing */
+/** @cond INTERNAL_MACRO */
+RUBY_EXTERN VALUE rb_memory_view_exported_object_registry;
+RUBY_EXTERN const rb_data_type_t rb_memory_view_exported_object_registry_data_type;
+/** @endcond */
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE()
+/**
+ * Return `true` if the data in the MemoryView `view` is row-major or
+ * column-major contiguous.
+ *
+ * Return `false` otherwise.
+ */
+static inline bool
+rb_memory_view_is_contiguous(const rb_memory_view_t *view)
+{
+ if (rb_memory_view_is_row_major_contiguous(view)) {
+ return true;
+ }
+ else if (rb_memory_view_is_column_major_contiguous(view)) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+#endif /* RUBY_BUFFER_H */
diff --git a/include/ruby/missing.h b/include/ruby/missing.h
new file mode 100644
index 0000000000..aea6c9088d
--- /dev/null
+++ b/include/ruby/missing.h
@@ -0,0 +1,342 @@
+#ifndef RUBY_MISSING_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_MISSING_H 1
+/**
+ * @author $Author$
+ * @date Sat May 11 23:46:03 JST 2002
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @brief Prototype for *.c in ./missing, and for missing timeval struct.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h>
+#endif
+
+#if defined(__cplusplus)
+# include <cmath>
+#else
+# include <math.h> /* for INFINITY and NAN */
+#endif
+
+#ifdef RUBY_ALTERNATIVE_MALLOC_HEADER
+# include RUBY_ALTERNATIVE_MALLOC_HEADER
+#endif
+
+#if defined(HAVE_TIME_H)
+# include <time.h>
+#endif
+
+#if defined(HAVE_SYS_TIME_H)
+# include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+# include <ieeefp.h>
+#endif
+
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/attr/format.h"
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+#ifndef M_PI_2
+# define M_PI_2 (M_PI/2)
+#endif
+
+#if !defined(HAVE_STRUCT_TIMEVAL)
+struct timeval {
+ time_t tv_sec; /* seconds */
+ long tv_usec; /* microseconds */
+};
+#endif /* HAVE_STRUCT_TIMEVAL */
+
+#if !defined(HAVE_STRUCT_TIMESPEC)
+/* :BEWARE: @shyouhei warns that IT IS A WRONG IDEA to define our own version
+ * of struct timespec here. `clock_gettime` is a system call, and your kernel
+ * could expect something other than just `long` (results stack smashing if
+ * that happens). See also https://ewontfix.com/19/ */
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+#endif
+
+#if !defined(HAVE_STRUCT_TIMEZONE)
+struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+};
+#endif
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+#ifndef HAVE_ACOSH
+RUBY_EXTERN double acosh(double);
+RUBY_EXTERN double asinh(double);
+RUBY_EXTERN double atanh(double);
+#endif
+
+#ifndef HAVE_CRYPT
+RUBY_EXTERN char *crypt(const char *, const char *);
+#endif
+
+#ifndef HAVE_EACCESS
+RUBY_EXTERN int eaccess(const char*, int);
+#endif
+
+#ifndef HAVE_ROUND
+RUBY_EXTERN double round(double); /* numeric.c */
+#endif
+
+#ifndef HAVE_FLOCK
+RUBY_EXTERN int flock(int, int);
+#endif
+
+/*
+#ifndef HAVE_FREXP
+RUBY_EXTERN double frexp(double, int *);
+#endif
+*/
+
+#ifndef HAVE_HYPOT
+RUBY_EXTERN double hypot(double, double);
+#endif
+
+#ifndef HAVE_ERF
+RUBY_EXTERN double erf(double);
+RUBY_EXTERN double erfc(double);
+#endif
+
+#ifndef HAVE_TGAMMA
+RUBY_EXTERN double tgamma(double);
+#endif
+
+#ifndef HAVE_LGAMMA_R
+RUBY_EXTERN double lgamma_r(double, int *);
+#endif
+
+#ifndef HAVE_CBRT
+RUBY_EXTERN double cbrt(double);
+#endif
+
+#if !defined(INFINITY) || !defined(NAN)
+union bytesequence4_or_float {
+ unsigned char bytesequence[4];
+ float float_value;
+};
+#endif
+
+#ifndef INFINITY
+/** @internal */
+RUBY_EXTERN const union bytesequence4_or_float rb_infinity;
+# define INFINITY (rb_infinity.float_value)
+# define USE_RB_INFINITY 1
+#endif
+
+#ifndef NAN
+/** @internal */
+RUBY_EXTERN const union bytesequence4_or_float rb_nan;
+# define NAN (rb_nan.float_value)
+# define USE_RB_NAN 1
+#endif
+
+#ifndef HUGE_VAL
+# define HUGE_VAL ((double)INFINITY)
+#endif
+
+#ifndef HAVE_FINITE
+# define HAVE_FINITE 1
+# define finite(x) isfinite(x)
+#endif
+
+#ifndef HAVE_NAN
+RUBY_EXTERN double nan(const char *);
+#endif
+
+#ifndef HAVE_NEXTAFTER
+RUBY_EXTERN double nextafter(double x, double y);
+#endif
+
+/*
+#ifndef HAVE_MEMCMP
+RUBY_EXTERN int memcmp(const void *, const void *, size_t);
+#endif
+*/
+
+#ifndef HAVE_MEMMOVE
+RUBY_EXTERN void *memmove(void *, const void *, size_t);
+#endif
+
+/*
+#ifndef HAVE_MODF
+RUBY_EXTERN double modf(double, double *);
+#endif
+*/
+
+#ifndef HAVE_STRCHR
+RUBY_EXTERN char *strchr(const char *, int);
+RUBY_EXTERN char *strrchr(const char *, int);
+#endif
+
+#ifndef HAVE_STRERROR
+RUBY_EXTERN char *strerror(int);
+#endif
+
+#ifndef HAVE_STRSTR
+RUBY_EXTERN char *strstr(const char *, const char *);
+#endif
+
+#ifndef HAVE_STRLCPY
+RUBY_EXTERN size_t strlcpy(char *, const char*, size_t);
+#endif
+
+#ifndef HAVE_STRLCAT
+RUBY_EXTERN size_t strlcat(char *, const char*, size_t);
+#endif
+
+#ifndef HAVE_FFS
+RUBY_EXTERN int ffs(int);
+#endif
+
+#ifdef BROKEN_CLOSE
+# include <sys/types.h>
+# include <sys/socket.h>
+RUBY_EXTERN int ruby_getpeername(int, struct sockaddr *, socklen_t *);
+RUBY_EXTERN int ruby_getsockname(int, struct sockaddr *, socklen_t *);
+RUBY_EXTERN int ruby_shutdown(int, int);
+RUBY_EXTERN int ruby_close(int);
+#endif
+
+#ifndef HAVE_SETPROCTITLE
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
+RUBY_EXTERN void setproctitle(const char *fmt, ...);
+#endif
+
+#ifdef HAVE_EXPLICIT_BZERO
+# /* Take that. */
+#elif defined(SecureZeroMemory)
+# define explicit_bzero(b, len) SecureZeroMemory(b, len)
+#else
+RUBY_EXTERN void explicit_bzero(void *b, size_t len);
+#endif
+
+#ifndef HAVE_TZSET
+RUBY_EXTERN void tzset(void);
+#endif
+
+#ifndef HAVE_POSIX_MADVISE
+RUBY_EXTERN int posix_madvise(void *, size_t, int);
+#endif
+
+#ifndef HAVE_GETEUID
+RUBY_EXTERN rb_uid_t geteuid(void);
+#endif
+
+#ifndef HAVE_GETUID
+RUBY_EXTERN rb_uid_t getuid(void);
+#endif
+
+#ifndef HAVE_GETEGID
+RUBY_EXTERN rb_gid_t getegid(void);
+#endif
+
+#ifndef HAVE_GETGID
+RUBY_EXTERN rb_gid_t getgid(void);
+#endif
+
+#ifndef HAVE_GETLOGIN
+RUBY_EXTERN char *getlogin(void);
+#endif
+
+#ifndef HAVE_GETPPID
+RUBY_EXTERN rb_pid_t getppid(void);
+#endif
+
+#ifndef HAVE_UMASK
+RUBY_EXTERN rb_mode_t umask(rb_mode_t);
+#endif
+
+#ifndef HAVE_CHMOD
+RUBY_EXTERN int chmod(const char *, rb_mode_t);
+#endif
+
+#ifndef HAVE_CHOWN
+RUBY_EXTERN int chown(const char *, rb_uid_t, rb_gid_t);
+#endif
+
+#ifndef HAVE_PCLOSE
+RUBY_EXTERN int pclose(FILE *);
+#endif
+
+#ifndef HAVE_POPEN
+RUBY_EXTERN FILE *popen(const char *, const char *);
+#endif
+
+#ifndef HAVE_PIPE
+RUBY_EXTERN int pipe(int [2]);
+#endif
+
+#ifndef HAVE_DUP
+RUBY_EXTERN int dup(int);
+#endif
+
+#ifndef HAVE_DUP2
+RUBY_EXTERN int dup2(int, int);
+#endif
+
+#ifndef HAVE_KILL
+RUBY_EXTERN int kill(rb_pid_t, int);
+#endif
+
+#ifndef HAVE_EXECL
+RUBY_EXTERN int execl(const char *, const char *, ...);
+#endif
+
+#ifndef HAVE_EXECLE
+RUBY_EXTERN int execle(const char *, const char *, ...);
+#endif
+
+#ifndef HAVE_EXECV
+RUBY_EXTERN int execv(const char *, char *const []);
+#endif
+
+#ifndef HAVE_EXECVE
+RUBY_EXTERN int execve(const char *, char *const [], char *const []);
+#endif
+
+#ifndef HAVE_SHUTDOWN
+RUBY_EXTERN int shutdown(int, int);
+#endif
+
+#ifndef HAVE_SYSTEM
+RUBY_EXTERN int system(const char *);
+#endif
+
+#ifndef WNOHANG
+# define WNOHANG 0
+#endif
+
+#ifndef HAVE_WAITPID
+# define HAVE_WAITPID 1
+RUBY_EXTERN rb_pid_t waitpid(rb_pid_t, int *, int);
+#endif
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_MISSING_H */
diff --git a/include/ruby/onigmo.h b/include/ruby/onigmo.h
new file mode 100644
index 0000000000..9dcddee829
--- /dev/null
+++ b/include/ruby/onigmo.h
@@ -0,0 +1,953 @@
+#ifndef ONIGMO_H
+#define ONIGMO_H
+/**********************************************************************
+ onigmo.h - Onigmo (Oniguruma-mod) (regular expression library)
+**********************************************************************/
+/*-
+ * Copyright (c) 2002-2016 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ * Copyright (c) 2011-2019 K.Takata <kentkt AT csc DOT jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+# if 0
+} /* satisfy cc-mode */
+# endif
+#endif
+
+#define ONIGMO_VERSION_MAJOR 6
+#define ONIGMO_VERSION_MINOR 2
+#define ONIGMO_VERSION_TEENY 0
+
+#ifndef ONIG_EXTERN
+# ifdef RUBY_EXTERN
+# define ONIG_EXTERN RUBY_EXTERN
+# else
+# if defined(_WIN32) && !defined(__GNUC__)
+# if defined(EXPORT) || defined(RUBY_EXPORT)
+# define ONIG_EXTERN extern __declspec(dllexport)
+# else
+# define ONIG_EXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif
+#endif
+
+#ifndef ONIG_EXTERN
+# define ONIG_EXTERN extern
+#endif
+
+#ifndef RUBY
+# ifndef RUBY_SYMBOL_EXPORT_BEGIN
+# define RUBY_SYMBOL_EXPORT_BEGIN
+# define RUBY_SYMBOL_EXPORT_END
+# endif
+#endif
+
+RUBY_SYMBOL_EXPORT_BEGIN
+
+#include <stddef.h> /* for size_t */
+
+/* PART: character encoding */
+
+#ifndef ONIG_ESCAPE_UCHAR_COLLISION
+# define UChar OnigUChar
+#endif
+
+typedef unsigned char OnigUChar;
+typedef unsigned int OnigCodePoint;
+typedef unsigned int OnigCtype;
+typedef size_t OnigDistance;
+typedef ptrdiff_t OnigPosition;
+
+#define ONIG_INFINITE_DISTANCE ~((OnigDistance )0)
+
+/*
+ * Onig casefold/case mapping flags and related definitions
+ *
+ * Subfields (starting with 0 at LSB):
+ * 0-2: Code point count in casefold.h
+ * 3-12: Index into SpecialCaseMapping array in casefold.h
+ * 13-22: Case folding/mapping flags
+ */
+typedef unsigned int OnigCaseFoldType; /* case fold flag */
+
+ONIG_EXTERN OnigCaseFoldType OnigDefaultCaseFoldFlag;
+
+/* bits for actual code point count; 3 bits is more than enough, currently only 2 used */
+#define OnigCodePointMaskWidth 3
+#define OnigCodePointMask ((1<<OnigCodePointMaskWidth)-1)
+#define OnigCodePointCount(n) ((n)&OnigCodePointMask)
+#define OnigCaseFoldFlags(n) ((n)&~OnigCodePointMask)
+
+/* #define ONIGENC_CASE_FOLD_HIRAGANA_KATAKANA (1<<1) */ /* no longer usable with these values! */
+/* #define ONIGENC_CASE_FOLD_KATAKANA_WIDTH (1<<2) */ /* no longer usable with these values! */
+
+/* bits for index into table with separate titlecase mappings */
+/* 10 bits provide 1024 values */
+#define OnigSpecialIndexShift 3
+#define OnigSpecialIndexWidth 10
+
+#define ONIGENC_CASE_UPCASE (1<<13) /* has/needs uppercase mapping */
+#define ONIGENC_CASE_DOWNCASE (1<<14) /* has/needs lowercase mapping */
+#define ONIGENC_CASE_TITLECASE (1<<15) /* has/needs (special) titlecase mapping */
+#define ONIGENC_CASE_SPECIAL_OFFSET 3 /* offset in bits from ONIGENC_CASE to ONIGENC_CASE_SPECIAL */
+#define ONIGENC_CASE_UP_SPECIAL (1<<16) /* has special upcase mapping */
+#define ONIGENC_CASE_DOWN_SPECIAL (1<<17) /* has special downcase mapping */
+#define ONIGENC_CASE_MODIFIED (1<<18) /* data has been modified */
+#define ONIGENC_CASE_FOLD (1<<19) /* has/needs case folding */
+
+#define ONIGENC_CASE_FOLD_TURKISH_AZERI (1<<20) /* needs mapping specific to Turkic languages; better not change original value! */
+
+#define ONIGENC_CASE_FOLD_LITHUANIAN (1<<21) /* needs Lithuanian-specific mapping */
+#define ONIGENC_CASE_ASCII_ONLY (1<<22) /* only modify ASCII range */
+#define ONIGENC_CASE_IS_TITLECASE (1<<23) /* character itself is already titlecase */
+
+#define INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR (1<<30) /* better not change original value! */
+
+#define ONIGENC_CASE_FOLD_MIN INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR
+#define ONIGENC_CASE_FOLD_DEFAULT OnigDefaultCaseFoldFlag
+
+
+#define ONIGENC_MAX_COMP_CASE_FOLD_CODE_LEN 3
+#define ONIGENC_GET_CASE_FOLD_CODES_MAX_NUM 13
+/* 13 => Unicode:0x1ffc */
+
+/* code range */
+#define ONIGENC_CODE_RANGE_NUM(range) ((int )range[0])
+#define ONIGENC_CODE_RANGE_FROM(range,i) range[((i)*2) + 1]
+#define ONIGENC_CODE_RANGE_TO(range,i) range[((i)*2) + 2]
+
+typedef struct {
+ int byte_len; /* argument(original) character(s) byte length */
+ int code_len; /* number of code */
+ OnigCodePoint code[ONIGENC_MAX_COMP_CASE_FOLD_CODE_LEN];
+} OnigCaseFoldCodeItem;
+
+typedef struct {
+ OnigCodePoint esc;
+ OnigCodePoint anychar;
+ OnigCodePoint anytime;
+ OnigCodePoint zero_or_one_time;
+ OnigCodePoint one_or_more_time;
+ OnigCodePoint anychar_anytime;
+} OnigMetaCharTableType;
+
+typedef int (*OnigApplyAllCaseFoldFunc)(OnigCodePoint from, OnigCodePoint* to, int to_len, void* arg);
+
+typedef struct OnigEncodingTypeST {
+ int (*precise_mbc_enc_len)(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
+ const char* name;
+ int max_enc_len;
+ int min_enc_len;
+ int (*is_mbc_newline)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
+ OnigCodePoint (*mbc_to_code)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
+ int (*code_to_mbclen)(OnigCodePoint code, const struct OnigEncodingTypeST* enc);
+ int (*code_to_mbc)(OnigCodePoint code, OnigUChar *buf, const struct OnigEncodingTypeST* enc);
+ int (*mbc_case_fold)(OnigCaseFoldType flag, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, const struct OnigEncodingTypeST* enc);
+ int (*apply_all_case_fold)(OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg, const struct OnigEncodingTypeST* enc);
+ int (*get_case_fold_codes_by_str)(OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem acs[], const struct OnigEncodingTypeST* enc);
+ int (*property_name_to_ctype)(const struct OnigEncodingTypeST* enc, const OnigUChar* p, const OnigUChar* end);
+ int (*is_code_ctype)(OnigCodePoint code, OnigCtype ctype, const struct OnigEncodingTypeST* enc);
+ int (*get_ctype_code_range)(OnigCtype ctype, OnigCodePoint* sb_out, const OnigCodePoint* ranges[], const struct OnigEncodingTypeST* enc);
+ OnigUChar* (*left_adjust_char_head)(const OnigUChar* start, const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
+ int (*is_allowed_reverse_match)(const OnigUChar* p, const OnigUChar* end, const struct OnigEncodingTypeST* enc);
+ int (*case_map)(OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc);
+ int ruby_encoding_index;
+ unsigned int flags;
+} OnigEncodingType;
+
+typedef const OnigEncodingType* OnigEncoding;
+
+ONIG_EXTERN const OnigEncodingType OnigEncodingASCII;
+#ifndef RUBY
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_1;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_2;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_3;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_4;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_5;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_6;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_7;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_8;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_9;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_10;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_11;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_13;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_14;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_15;
+ONIG_EXTERN const OnigEncodingType OnigEncodingISO_8859_16;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_8;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_16BE;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_16LE;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_32BE;
+ONIG_EXTERN const OnigEncodingType OnigEncodingUTF_32LE;
+ONIG_EXTERN const OnigEncodingType OnigEncodingEUC_JP;
+ONIG_EXTERN const OnigEncodingType OnigEncodingEUC_TW;
+ONIG_EXTERN const OnigEncodingType OnigEncodingEUC_KR;
+ONIG_EXTERN const OnigEncodingType OnigEncodingEUC_CN;
+ONIG_EXTERN const OnigEncodingType OnigEncodingShift_JIS;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_31J;
+/* ONIG_EXTERN const OnigEncodingType OnigEncodingKOI8; */
+ONIG_EXTERN const OnigEncodingType OnigEncodingKOI8_R;
+ONIG_EXTERN const OnigEncodingType OnigEncodingKOI8_U;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1250;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1251;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1252;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1253;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1254;
+ONIG_EXTERN const OnigEncodingType OnigEncodingWindows_1257;
+ONIG_EXTERN const OnigEncodingType OnigEncodingBIG5;
+ONIG_EXTERN const OnigEncodingType OnigEncodingGB18030;
+#endif /* RUBY */
+
+#define ONIG_ENCODING_ASCII (&OnigEncodingASCII)
+#ifndef RUBY
+# define ONIG_ENCODING_ISO_8859_1 (&OnigEncodingISO_8859_1)
+# define ONIG_ENCODING_ISO_8859_2 (&OnigEncodingISO_8859_2)
+# define ONIG_ENCODING_ISO_8859_3 (&OnigEncodingISO_8859_3)
+# define ONIG_ENCODING_ISO_8859_4 (&OnigEncodingISO_8859_4)
+# define ONIG_ENCODING_ISO_8859_5 (&OnigEncodingISO_8859_5)
+# define ONIG_ENCODING_ISO_8859_6 (&OnigEncodingISO_8859_6)
+# define ONIG_ENCODING_ISO_8859_7 (&OnigEncodingISO_8859_7)
+# define ONIG_ENCODING_ISO_8859_8 (&OnigEncodingISO_8859_8)
+# define ONIG_ENCODING_ISO_8859_9 (&OnigEncodingISO_8859_9)
+# define ONIG_ENCODING_ISO_8859_10 (&OnigEncodingISO_8859_10)
+# define ONIG_ENCODING_ISO_8859_11 (&OnigEncodingISO_8859_11)
+# define ONIG_ENCODING_ISO_8859_13 (&OnigEncodingISO_8859_13)
+# define ONIG_ENCODING_ISO_8859_14 (&OnigEncodingISO_8859_14)
+# define ONIG_ENCODING_ISO_8859_15 (&OnigEncodingISO_8859_15)
+# define ONIG_ENCODING_ISO_8859_16 (&OnigEncodingISO_8859_16)
+# define ONIG_ENCODING_UTF_8 (&OnigEncodingUTF_8)
+# define ONIG_ENCODING_UTF_16BE (&OnigEncodingUTF_16BE)
+# define ONIG_ENCODING_UTF_16LE (&OnigEncodingUTF_16LE)
+# define ONIG_ENCODING_UTF_32BE (&OnigEncodingUTF_32BE)
+# define ONIG_ENCODING_UTF_32LE (&OnigEncodingUTF_32LE)
+# define ONIG_ENCODING_EUC_JP (&OnigEncodingEUC_JP)
+# define ONIG_ENCODING_EUC_TW (&OnigEncodingEUC_TW)
+# define ONIG_ENCODING_EUC_KR (&OnigEncodingEUC_KR)
+# define ONIG_ENCODING_EUC_CN (&OnigEncodingEUC_CN)
+# define ONIG_ENCODING_SHIFT_JIS (&OnigEncodingShift_JIS)
+# define ONIG_ENCODING_WINDOWS_31J (&OnigEncodingWindows_31J)
+/* # define ONIG_ENCODING_KOI8 (&OnigEncodingKOI8) */
+# define ONIG_ENCODING_KOI8_R (&OnigEncodingKOI8_R)
+# define ONIG_ENCODING_KOI8_U (&OnigEncodingKOI8_U)
+# define ONIG_ENCODING_WINDOWS_1250 (&OnigEncodingWindows_1250)
+# define ONIG_ENCODING_WINDOWS_1251 (&OnigEncodingWindows_1251)
+# define ONIG_ENCODING_WINDOWS_1252 (&OnigEncodingWindows_1252)
+# define ONIG_ENCODING_WINDOWS_1253 (&OnigEncodingWindows_1253)
+# define ONIG_ENCODING_WINDOWS_1254 (&OnigEncodingWindows_1254)
+# define ONIG_ENCODING_WINDOWS_1257 (&OnigEncodingWindows_1257)
+# define ONIG_ENCODING_BIG5 (&OnigEncodingBIG5)
+# define ONIG_ENCODING_GB18030 (&OnigEncodingGB18030)
+
+/* old names */
+# define ONIG_ENCODING_SJIS ONIG_ENCODING_SHIFT_JIS
+# define ONIG_ENCODING_CP932 ONIG_ENCODING_WINDOWS_31J
+# define ONIG_ENCODING_CP1250 ONIG_ENCODING_WINDOWS_1250
+# define ONIG_ENCODING_CP1251 ONIG_ENCODING_WINDOWS_1251
+# define ONIG_ENCODING_CP1252 ONIG_ENCODING_WINDOWS_1252
+# define ONIG_ENCODING_CP1253 ONIG_ENCODING_WINDOWS_1253
+# define ONIG_ENCODING_CP1254 ONIG_ENCODING_WINDOWS_1254
+# define ONIG_ENCODING_CP1257 ONIG_ENCODING_WINDOWS_1257
+# define ONIG_ENCODING_UTF8 ONIG_ENCODING_UTF_8
+# define ONIG_ENCODING_UTF16_BE ONIG_ENCODING_UTF_16BE
+# define ONIG_ENCODING_UTF16_LE ONIG_ENCODING_UTF_16LE
+# define ONIG_ENCODING_UTF32_BE ONIG_ENCODING_UTF_32BE
+# define ONIG_ENCODING_UTF32_LE ONIG_ENCODING_UTF_32LE
+#endif /* RUBY */
+
+#define ONIG_ENCODING_UNDEF ((OnigEncoding )0)
+
+/* this declaration needs to be here because it is used in string.c in Ruby */
+ONIG_EXTERN
+int onigenc_ascii_only_case_map(OnigCaseFoldType* flagP, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, OnigUChar* to_end, const struct OnigEncodingTypeST* enc);
+
+
+/* work size */
+#define ONIGENC_CODE_TO_MBC_MAXLEN 7
+#define ONIGENC_MBC_CASE_FOLD_MAXLEN 18
+/* 18: 6(max-byte) * 3(case-fold chars) */
+
+/* character types */
+#define ONIGENC_CTYPE_NEWLINE 0
+#define ONIGENC_CTYPE_ALPHA 1
+#define ONIGENC_CTYPE_BLANK 2
+#define ONIGENC_CTYPE_CNTRL 3
+#define ONIGENC_CTYPE_DIGIT 4
+#define ONIGENC_CTYPE_GRAPH 5
+#define ONIGENC_CTYPE_LOWER 6
+#define ONIGENC_CTYPE_PRINT 7
+#define ONIGENC_CTYPE_PUNCT 8
+#define ONIGENC_CTYPE_SPACE 9
+#define ONIGENC_CTYPE_UPPER 10
+#define ONIGENC_CTYPE_XDIGIT 11
+#define ONIGENC_CTYPE_WORD 12
+#define ONIGENC_CTYPE_ALNUM 13 /* alpha || digit */
+#define ONIGENC_CTYPE_ASCII 14
+#define ONIGENC_MAX_STD_CTYPE ONIGENC_CTYPE_ASCII
+
+/* flags */
+#define ONIGENC_FLAG_NONE 0U
+#define ONIGENC_FLAG_UNICODE 1U
+
+#define onig_enc_len(enc,p,e) ONIGENC_MBC_ENC_LEN(enc, p, e)
+
+#define ONIGENC_IS_UNDEF(enc) ((enc) == ONIG_ENCODING_UNDEF)
+#define ONIGENC_IS_SINGLEBYTE(enc) (ONIGENC_MBC_MAXLEN(enc) == 1)
+#define ONIGENC_IS_MBC_HEAD(enc,p,e) (ONIGENC_MBC_ENC_LEN(enc,p,e) != 1)
+#define ONIGENC_IS_MBC_ASCII(p) (*(p) < 128)
+#define ONIGENC_IS_CODE_ASCII(code) ((code) < 128)
+#define ONIGENC_IS_MBC_WORD(enc,s,end) \
+ ONIGENC_IS_CODE_WORD(enc,ONIGENC_MBC_TO_CODE(enc,s,end))
+#define ONIGENC_IS_MBC_ASCII_WORD(enc,s,end) \
+ onigenc_ascii_is_code_ctype( \
+ ONIGENC_MBC_TO_CODE(enc,s,end),ONIGENC_CTYPE_WORD,enc)
+#define ONIGENC_IS_UNICODE(enc) ((enc)->flags & ONIGENC_FLAG_UNICODE)
+
+
+#define ONIGENC_NAME(enc) ((enc)->name)
+
+#define ONIGENC_MBC_CASE_FOLD(enc,flag,pp,end,buf) \
+ (enc)->mbc_case_fold(flag,(const OnigUChar** )pp,end,buf,enc)
+#define ONIGENC_IS_ALLOWED_REVERSE_MATCH(enc,s,end) \
+ (enc)->is_allowed_reverse_match(s,end,enc)
+#define ONIGENC_LEFT_ADJUST_CHAR_HEAD(enc,start,s,end) \
+ (enc)->left_adjust_char_head(start, s, end, enc)
+#define ONIGENC_APPLY_ALL_CASE_FOLD(enc,case_fold_flag,f,arg) \
+ (enc)->apply_all_case_fold(case_fold_flag,f,arg,enc)
+#define ONIGENC_GET_CASE_FOLD_CODES_BY_STR(enc,case_fold_flag,p,end,acs) \
+ (enc)->get_case_fold_codes_by_str(case_fold_flag,p,end,acs,enc)
+#define ONIGENC_STEP_BACK(enc,start,s,end,n) \
+ onigenc_step_back((enc),(start),(s),(end),(n))
+
+#define ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(n) (n)
+#define ONIGENC_MBCLEN_CHARFOUND_P(r) (0 < (r))
+#define ONIGENC_MBCLEN_CHARFOUND_LEN(r) (r)
+
+#define ONIGENC_CONSTRUCT_MBCLEN_INVALID() (-1)
+#define ONIGENC_MBCLEN_INVALID_P(r) ((r) == -1)
+
+#define ONIGENC_CONSTRUCT_MBCLEN_NEEDMORE(n) (-1-(n))
+#define ONIGENC_MBCLEN_NEEDMORE_P(r) ((r) < -1)
+#define ONIGENC_MBCLEN_NEEDMORE_LEN(r) (-1-(r))
+
+#define ONIGENC_PRECISE_MBC_ENC_LEN(enc,p,e) (enc)->precise_mbc_enc_len(p,e,enc)
+
+ONIG_EXTERN
+int onigenc_mbclen(const OnigUChar* p,const OnigUChar* e, const struct OnigEncodingTypeST* enc);
+
+#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen(p,e,enc)
+#define ONIGENC_MBC_MAXLEN(enc) ((enc)->max_enc_len)
+#define ONIGENC_MBC_MAXLEN_DIST(enc) ONIGENC_MBC_MAXLEN(enc)
+#define ONIGENC_MBC_MINLEN(enc) ((enc)->min_enc_len)
+#define ONIGENC_IS_MBC_NEWLINE(enc,p,end) (enc)->is_mbc_newline((p),(end),enc)
+#define ONIGENC_MBC_TO_CODE(enc,p,end) (enc)->mbc_to_code((p),(end),enc)
+#define ONIGENC_CODE_TO_MBCLEN(enc,code) (enc)->code_to_mbclen(code,enc)
+#define ONIGENC_CODE_TO_MBC(enc,code,buf) (enc)->code_to_mbc(code,buf,enc)
+#define ONIGENC_PROPERTY_NAME_TO_CTYPE(enc,p,end) \
+ (enc)->property_name_to_ctype(enc,p,end)
+
+#define ONIGENC_IS_CODE_CTYPE(enc,code,ctype) (enc)->is_code_ctype(code,ctype,enc)
+
+#define ONIGENC_IS_CODE_NEWLINE(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_NEWLINE)
+#define ONIGENC_IS_CODE_GRAPH(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_GRAPH)
+#define ONIGENC_IS_CODE_PRINT(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_PRINT)
+#define ONIGENC_IS_CODE_ALNUM(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_ALNUM)
+#define ONIGENC_IS_CODE_ALPHA(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_ALPHA)
+#define ONIGENC_IS_CODE_LOWER(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_LOWER)
+#define ONIGENC_IS_CODE_UPPER(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_UPPER)
+#define ONIGENC_IS_CODE_CNTRL(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_CNTRL)
+#define ONIGENC_IS_CODE_PUNCT(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_PUNCT)
+#define ONIGENC_IS_CODE_SPACE(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_SPACE)
+#define ONIGENC_IS_CODE_BLANK(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_BLANK)
+#define ONIGENC_IS_CODE_DIGIT(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_DIGIT)
+#define ONIGENC_IS_CODE_XDIGIT(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_XDIGIT)
+#define ONIGENC_IS_CODE_WORD(enc,code) \
+ ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_WORD)
+
+#define ONIGENC_GET_CTYPE_CODE_RANGE(enc,ctype,sbout,ranges) \
+ (enc)->get_ctype_code_range(ctype,sbout,ranges,enc)
+
+ONIG_EXTERN
+OnigUChar* onigenc_step_back(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end, int n);
+
+
+/* encoding API */
+ONIG_EXTERN
+int onigenc_init(void);
+ONIG_EXTERN
+int onigenc_set_default_encoding(OnigEncoding enc);
+ONIG_EXTERN
+OnigEncoding onigenc_get_default_encoding(void);
+ONIG_EXTERN
+OnigUChar* onigenc_get_right_adjust_char_head_with_prev(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end, const OnigUChar** prev);
+ONIG_EXTERN
+OnigUChar* onigenc_get_prev_char_head(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end);
+ONIG_EXTERN
+OnigUChar* onigenc_get_left_adjust_char_head(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end);
+ONIG_EXTERN
+OnigUChar* onigenc_get_right_adjust_char_head(OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end);
+ONIG_EXTERN
+int onigenc_strlen(OnigEncoding enc, const OnigUChar* p, const OnigUChar* end);
+ONIG_EXTERN
+int onigenc_strlen_null(OnigEncoding enc, const OnigUChar* p);
+ONIG_EXTERN
+int onigenc_str_bytelen_null(OnigEncoding enc, const OnigUChar* p);
+
+
+
+/* PART: regular expression */
+
+/* config parameters */
+#define ONIG_NREGION 4
+#define ONIG_MAX_CAPTURE_GROUP_NUM 32767
+#define ONIG_MAX_BACKREF_NUM 1000
+#define ONIG_MAX_REPEAT_NUM 100000
+#define ONIG_MAX_MULTI_BYTE_RANGES_NUM 10000
+/* constants */
+#define ONIG_MAX_ERROR_MESSAGE_LEN 90
+
+typedef unsigned int OnigOptionType;
+
+#define ONIG_OPTION_DEFAULT ONIG_OPTION_NONE
+
+/* options */
+#define ONIG_OPTION_NONE 0U
+#define ONIG_OPTION_IGNORECASE 1U
+#define ONIG_OPTION_EXTEND (ONIG_OPTION_IGNORECASE << 1)
+#define ONIG_OPTION_MULTILINE (ONIG_OPTION_EXTEND << 1)
+#define ONIG_OPTION_DOTALL ONIG_OPTION_MULTILINE
+#define ONIG_OPTION_SINGLELINE (ONIG_OPTION_MULTILINE << 1)
+#define ONIG_OPTION_FIND_LONGEST (ONIG_OPTION_SINGLELINE << 1)
+#define ONIG_OPTION_FIND_NOT_EMPTY (ONIG_OPTION_FIND_LONGEST << 1)
+#define ONIG_OPTION_NEGATE_SINGLELINE (ONIG_OPTION_FIND_NOT_EMPTY << 1)
+#define ONIG_OPTION_DONT_CAPTURE_GROUP (ONIG_OPTION_NEGATE_SINGLELINE << 1)
+#define ONIG_OPTION_CAPTURE_GROUP (ONIG_OPTION_DONT_CAPTURE_GROUP << 1)
+/* options (search time) */
+#define ONIG_OPTION_NOTBOL (ONIG_OPTION_CAPTURE_GROUP << 1)
+#define ONIG_OPTION_NOTEOL (ONIG_OPTION_NOTBOL << 1)
+#define ONIG_OPTION_NOTBOS (ONIG_OPTION_NOTEOL << 1)
+#define ONIG_OPTION_NOTEOS (ONIG_OPTION_NOTBOS << 1)
+/* options (ctype range) */
+#define ONIG_OPTION_ASCII_RANGE (ONIG_OPTION_NOTEOS << 1)
+#define ONIG_OPTION_POSIX_BRACKET_ALL_RANGE (ONIG_OPTION_ASCII_RANGE << 1)
+#define ONIG_OPTION_WORD_BOUND_ALL_RANGE (ONIG_OPTION_POSIX_BRACKET_ALL_RANGE << 1)
+/* options (newline) */
+#define ONIG_OPTION_NEWLINE_CRLF (ONIG_OPTION_WORD_BOUND_ALL_RANGE << 1)
+#define ONIG_OPTION_MAXBIT ONIG_OPTION_NEWLINE_CRLF /* limit */
+
+#define ONIG_OPTION_ON(options,regopt) ((options) |= (regopt))
+#define ONIG_OPTION_OFF(options,regopt) ((options) &= ~(regopt))
+#define ONIG_IS_OPTION_ON(options,option) ((options) & (option))
+
+/* syntax */
+typedef struct {
+ unsigned int op;
+ unsigned int op2;
+ unsigned int behavior;
+ OnigOptionType options; /* default option */
+ OnigMetaCharTableType meta_char_table;
+} OnigSyntaxType;
+
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxASIS;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPosixBasic;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPosixExtended;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxEmacs;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxGrep;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxGnuRegex;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxJava;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl58;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl58_NG;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxRuby;
+ONIG_EXTERN const OnigSyntaxType OnigSyntaxPython;
+
+/* predefined syntaxes (see regsyntax.c) */
+#define ONIG_SYNTAX_ASIS (&OnigSyntaxASIS)
+#define ONIG_SYNTAX_POSIX_BASIC (&OnigSyntaxPosixBasic)
+#define ONIG_SYNTAX_POSIX_EXTENDED (&OnigSyntaxPosixExtended)
+#define ONIG_SYNTAX_EMACS (&OnigSyntaxEmacs)
+#define ONIG_SYNTAX_GREP (&OnigSyntaxGrep)
+#define ONIG_SYNTAX_GNU_REGEX (&OnigSyntaxGnuRegex)
+#define ONIG_SYNTAX_JAVA (&OnigSyntaxJava)
+#define ONIG_SYNTAX_PERL58 (&OnigSyntaxPerl58)
+#define ONIG_SYNTAX_PERL58_NG (&OnigSyntaxPerl58_NG)
+#define ONIG_SYNTAX_PERL (&OnigSyntaxPerl)
+#define ONIG_SYNTAX_RUBY (&OnigSyntaxRuby)
+#define ONIG_SYNTAX_PYTHON (&OnigSyntaxPython)
+
+/* default syntax */
+ONIG_EXTERN const OnigSyntaxType* OnigDefaultSyntax;
+#define ONIG_SYNTAX_DEFAULT OnigDefaultSyntax
+
+/* syntax (operators) */
+#define ONIG_SYN_OP_VARIABLE_META_CHARACTERS (1U<<0)
+#define ONIG_SYN_OP_DOT_ANYCHAR (1U<<1) /* . */
+#define ONIG_SYN_OP_ASTERISK_ZERO_INF (1U<<2) /* * */
+#define ONIG_SYN_OP_ESC_ASTERISK_ZERO_INF (1U<<3)
+#define ONIG_SYN_OP_PLUS_ONE_INF (1U<<4) /* + */
+#define ONIG_SYN_OP_ESC_PLUS_ONE_INF (1U<<5)
+#define ONIG_SYN_OP_QMARK_ZERO_ONE (1U<<6) /* ? */
+#define ONIG_SYN_OP_ESC_QMARK_ZERO_ONE (1U<<7)
+#define ONIG_SYN_OP_BRACE_INTERVAL (1U<<8) /* {lower,upper} */
+#define ONIG_SYN_OP_ESC_BRACE_INTERVAL (1U<<9) /* \{lower,upper\} */
+#define ONIG_SYN_OP_VBAR_ALT (1U<<10) /* | */
+#define ONIG_SYN_OP_ESC_VBAR_ALT (1U<<11) /* \| */
+#define ONIG_SYN_OP_LPAREN_SUBEXP (1U<<12) /* (...) */
+#define ONIG_SYN_OP_ESC_LPAREN_SUBEXP (1U<<13) /* \(...\) */
+#define ONIG_SYN_OP_ESC_AZ_BUF_ANCHOR (1U<<14) /* \A, \Z, \z */
+#define ONIG_SYN_OP_ESC_CAPITAL_G_BEGIN_ANCHOR (1U<<15) /* \G */
+#define ONIG_SYN_OP_DECIMAL_BACKREF (1U<<16) /* \num */
+#define ONIG_SYN_OP_BRACKET_CC (1U<<17) /* [...] */
+#define ONIG_SYN_OP_ESC_W_WORD (1U<<18) /* \w, \W */
+#define ONIG_SYN_OP_ESC_LTGT_WORD_BEGIN_END (1U<<19) /* \<. \> */
+#define ONIG_SYN_OP_ESC_B_WORD_BOUND (1U<<20) /* \b, \B */
+#define ONIG_SYN_OP_ESC_S_WHITE_SPACE (1U<<21) /* \s, \S */
+#define ONIG_SYN_OP_ESC_D_DIGIT (1U<<22) /* \d, \D */
+#define ONIG_SYN_OP_LINE_ANCHOR (1U<<23) /* ^, $ */
+#define ONIG_SYN_OP_POSIX_BRACKET (1U<<24) /* [:xxxx:] */
+#define ONIG_SYN_OP_QMARK_NON_GREEDY (1U<<25) /* ??,*?,+?,{n,m}? */
+#define ONIG_SYN_OP_ESC_CONTROL_CHARS (1U<<26) /* \n,\r,\t,\a ... */
+#define ONIG_SYN_OP_ESC_C_CONTROL (1U<<27) /* \cx */
+#define ONIG_SYN_OP_ESC_OCTAL3 (1U<<28) /* \OOO */
+#define ONIG_SYN_OP_ESC_X_HEX2 (1U<<29) /* \xHH */
+#define ONIG_SYN_OP_ESC_X_BRACE_HEX8 (1U<<30) /* \x{7HHHHHHH} */
+#define ONIG_SYN_OP_ESC_O_BRACE_OCTAL (1U<<31) /* \o{OOO} */
+
+#define ONIG_SYN_OP2_ESC_CAPITAL_Q_QUOTE (1U<<0) /* \Q...\E */
+#define ONIG_SYN_OP2_QMARK_GROUP_EFFECT (1U<<1) /* (?...) */
+#define ONIG_SYN_OP2_OPTION_PERL (1U<<2) /* (?imsxadlu), (?-imsx), (?^imsxalu) */
+#define ONIG_SYN_OP2_OPTION_RUBY (1U<<3) /* (?imxadu), (?-imx) */
+#define ONIG_SYN_OP2_PLUS_POSSESSIVE_REPEAT (1U<<4) /* ?+,*+,++ */
+#define ONIG_SYN_OP2_PLUS_POSSESSIVE_INTERVAL (1U<<5) /* {n,m}+ */
+#define ONIG_SYN_OP2_CCLASS_SET_OP (1U<<6) /* [...&&..[..]..] */
+#define ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP (1U<<7) /* (?<name>...) */
+#define ONIG_SYN_OP2_ESC_K_NAMED_BACKREF (1U<<8) /* \k<name> */
+#define ONIG_SYN_OP2_ESC_G_SUBEXP_CALL (1U<<9) /* \g<name>, \g<n> */
+#define ONIG_SYN_OP2_ATMARK_CAPTURE_HISTORY (1U<<10) /* (?@..),(?@<x>..) */
+#define ONIG_SYN_OP2_ESC_CAPITAL_C_BAR_CONTROL (1U<<11) /* \C-x */
+#define ONIG_SYN_OP2_ESC_CAPITAL_M_BAR_META (1U<<12) /* \M-x */
+#define ONIG_SYN_OP2_ESC_V_VTAB (1U<<13) /* \v as VTAB */
+#define ONIG_SYN_OP2_ESC_U_HEX4 (1U<<14) /* \uHHHH */
+#define ONIG_SYN_OP2_ESC_GNU_BUF_ANCHOR (1U<<15) /* \`, \' */
+#define ONIG_SYN_OP2_ESC_P_BRACE_CHAR_PROPERTY (1U<<16) /* \p{...}, \P{...} */
+#define ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT (1U<<17) /* \p{^..}, \P{^..} */
+/* #define ONIG_SYN_OP2_CHAR_PROPERTY_PREFIX_IS (1U<<18) */
+#define ONIG_SYN_OP2_ESC_H_XDIGIT (1U<<19) /* \h, \H */
+#define ONIG_SYN_OP2_INEFFECTIVE_ESCAPE (1U<<20) /* \ */
+#define ONIG_SYN_OP2_ESC_CAPITAL_R_LINEBREAK (1U<<21) /* \R as (?>\x0D\x0A|[\x0A-\x0D\x{85}\x{2028}\x{2029}]) */
+#define ONIG_SYN_OP2_ESC_CAPITAL_X_EXTENDED_GRAPHEME_CLUSTER (1U<<22) /* \X */
+#define ONIG_SYN_OP2_ESC_V_VERTICAL_WHITESPACE (1U<<23) /* \v, \V -- Perl */ /* NOTIMPL */
+#define ONIG_SYN_OP2_ESC_H_HORIZONTAL_WHITESPACE (1U<<24) /* \h, \H -- Perl */ /* NOTIMPL */
+#define ONIG_SYN_OP2_ESC_CAPITAL_K_KEEP (1U<<25) /* \K */
+#define ONIG_SYN_OP2_ESC_G_BRACE_BACKREF (1U<<26) /* \g{name}, \g{n} */
+#define ONIG_SYN_OP2_QMARK_SUBEXP_CALL (1U<<27) /* (?&name), (?n), (?R), (?0) */
+#define ONIG_SYN_OP2_QMARK_VBAR_BRANCH_RESET (1U<<28) /* (?|...) */ /* NOTIMPL */
+#define ONIG_SYN_OP2_QMARK_LPAREN_CONDITION (1U<<29) /* (?(cond)yes...|no...) */
+#define ONIG_SYN_OP2_QMARK_CAPITAL_P_NAMED_GROUP (1U<<30) /* (?P<name>...), (?P=name), (?P>name) -- Python/PCRE */
+#define ONIG_SYN_OP2_QMARK_TILDE_ABSENT (1U<<31) /* (?~...) */
+/* #define ONIG_SYN_OP2_OPTION_JAVA (1U<<xx) */ /* (?idmsux), (?-idmsux) */ /* NOTIMPL */
+
+/* syntax (behavior) */
+#define ONIG_SYN_CONTEXT_INDEP_ANCHORS (1U<<31) /* not implemented */
+#define ONIG_SYN_CONTEXT_INDEP_REPEAT_OPS (1U<<0) /* ?, *, +, {n,m} */
+#define ONIG_SYN_CONTEXT_INVALID_REPEAT_OPS (1U<<1) /* error or ignore */
+#define ONIG_SYN_ALLOW_UNMATCHED_CLOSE_SUBEXP (1U<<2) /* ...)... */
+#define ONIG_SYN_ALLOW_INVALID_INTERVAL (1U<<3) /* {??? */
+#define ONIG_SYN_ALLOW_INTERVAL_LOW_ABBREV (1U<<4) /* {,n} => {0,n} */
+#define ONIG_SYN_STRICT_CHECK_BACKREF (1U<<5) /* /(\1)/,/\1()/ ..*/
+#define ONIG_SYN_DIFFERENT_LEN_ALT_LOOK_BEHIND (1U<<6) /* (?<=a|bc) */
+#define ONIG_SYN_CAPTURE_ONLY_NAMED_GROUP (1U<<7) /* see doc/RE */
+#define ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME (1U<<8) /* (?<x>)(?<x>) */
+#define ONIG_SYN_FIXED_INTERVAL_IS_GREEDY_ONLY (1U<<9) /* a{n}?=(?:a{n})? */
+#define ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME_CALL (1U<<10) /* (?<x>)(?<x>)(?&x) */
+#define ONIG_SYN_USE_LEFT_MOST_NAMED_GROUP (1U<<11) /* (?<x>)(?<x>)\k<x> */
+
+/* syntax (behavior) in char class [...] */
+#define ONIG_SYN_NOT_NEWLINE_IN_NEGATIVE_CC (1U<<20) /* [^...] */
+#define ONIG_SYN_BACKSLASH_ESCAPE_IN_CC (1U<<21) /* [..\w..] etc.. */
+#define ONIG_SYN_ALLOW_EMPTY_RANGE_IN_CC (1U<<22)
+#define ONIG_SYN_ALLOW_DOUBLE_RANGE_OP_IN_CC (1U<<23) /* [0-9-a]=[0-9\-a] */
+/* syntax (behavior) warning */
+#define ONIG_SYN_WARN_CC_OP_NOT_ESCAPED (1U<<24) /* [,-,] */
+#define ONIG_SYN_WARN_REDUNDANT_NESTED_REPEAT (1U<<25) /* (?:a*)+ */
+#define ONIG_SYN_WARN_CC_DUP (1U<<26) /* [aa] */
+
+/* meta character specifiers (onig_set_meta_char()) */
+#define ONIG_META_CHAR_ESCAPE 0
+#define ONIG_META_CHAR_ANYCHAR 1
+#define ONIG_META_CHAR_ANYTIME 2
+#define ONIG_META_CHAR_ZERO_OR_ONE_TIME 3
+#define ONIG_META_CHAR_ONE_OR_MORE_TIME 4
+#define ONIG_META_CHAR_ANYCHAR_ANYTIME 5
+
+#define ONIG_INEFFECTIVE_META_CHAR 0
+
+/* error codes */
+#define ONIG_IS_PATTERN_ERROR(ecode) ((ecode) <= -100 && (ecode) > -1000)
+/* normal return */
+#define ONIG_NORMAL 0
+#define ONIG_MISMATCH -1
+#define ONIG_NO_SUPPORT_CONFIG -2
+
+/* internal error */
+#define ONIGERR_MEMORY -5
+#define ONIGERR_TYPE_BUG -6
+#define ONIGERR_PARSER_BUG -11
+#define ONIGERR_STACK_BUG -12
+#define ONIGERR_UNDEFINED_BYTECODE -13
+#define ONIGERR_UNEXPECTED_BYTECODE -14
+#define ONIGERR_MATCH_STACK_LIMIT_OVER -15
+#define ONIGERR_PARSE_DEPTH_LIMIT_OVER -16
+#define ONIGERR_DEFAULT_ENCODING_IS_NOT_SET -21
+#define ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR -22
+#define ONIGERR_TIMEOUT -23
+/* general error */
+#define ONIGERR_INVALID_ARGUMENT -30
+/* syntax error */
+#define ONIGERR_END_PATTERN_AT_LEFT_BRACE -100
+#define ONIGERR_END_PATTERN_AT_LEFT_BRACKET -101
+#define ONIGERR_EMPTY_CHAR_CLASS -102
+#define ONIGERR_PREMATURE_END_OF_CHAR_CLASS -103
+#define ONIGERR_END_PATTERN_AT_ESCAPE -104
+#define ONIGERR_END_PATTERN_AT_META -105
+#define ONIGERR_END_PATTERN_AT_CONTROL -106
+#define ONIGERR_META_CODE_SYNTAX -108
+#define ONIGERR_CONTROL_CODE_SYNTAX -109
+#define ONIGERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE -110
+#define ONIGERR_CHAR_CLASS_VALUE_AT_START_OF_RANGE -111
+#define ONIGERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS -112
+#define ONIGERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED -113
+#define ONIGERR_TARGET_OF_REPEAT_OPERATOR_INVALID -114
+#define ONIGERR_NESTED_REPEAT_OPERATOR -115
+#define ONIGERR_UNMATCHED_CLOSE_PARENTHESIS -116
+#define ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS -117
+#define ONIGERR_END_PATTERN_IN_GROUP -118
+#define ONIGERR_UNDEFINED_GROUP_OPTION -119
+#define ONIGERR_INVALID_POSIX_BRACKET_TYPE -121
+#define ONIGERR_INVALID_LOOK_BEHIND_PATTERN -122
+#define ONIGERR_INVALID_REPEAT_RANGE_PATTERN -123
+#define ONIGERR_INVALID_CONDITION_PATTERN -124
+/* values error (syntax error) */
+#define ONIGERR_TOO_BIG_NUMBER -200
+#define ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE -201
+#define ONIGERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE -202
+#define ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS -203
+#define ONIGERR_MISMATCH_CODE_LENGTH_IN_CLASS_RANGE -204
+#define ONIGERR_TOO_MANY_MULTI_BYTE_RANGES -205
+#define ONIGERR_TOO_SHORT_MULTI_BYTE_STRING -206
+#define ONIGERR_TOO_BIG_BACKREF_NUMBER -207
+#define ONIGERR_INVALID_BACKREF -208
+#define ONIGERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED -209
+#define ONIGERR_TOO_MANY_CAPTURE_GROUPS -210
+#define ONIGERR_TOO_SHORT_DIGITS -211
+#define ONIGERR_TOO_LONG_WIDE_CHAR_VALUE -212
+#define ONIGERR_EMPTY_GROUP_NAME -214
+#define ONIGERR_INVALID_GROUP_NAME -215
+#define ONIGERR_INVALID_CHAR_IN_GROUP_NAME -216
+#define ONIGERR_UNDEFINED_NAME_REFERENCE -217
+#define ONIGERR_UNDEFINED_GROUP_REFERENCE -218
+#define ONIGERR_MULTIPLEX_DEFINED_NAME -219
+#define ONIGERR_MULTIPLEX_DEFINITION_NAME_CALL -220
+#define ONIGERR_NEVER_ENDING_RECURSION -221
+#define ONIGERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY -222
+#define ONIGERR_INVALID_CHAR_PROPERTY_NAME -223
+#define ONIGERR_INVALID_CODE_POINT_VALUE -400
+#define ONIGERR_INVALID_WIDE_CHAR_VALUE -400
+#define ONIGERR_TOO_BIG_WIDE_CHAR_VALUE -401
+#define ONIGERR_NOT_SUPPORTED_ENCODING_COMBINATION -402
+#define ONIGERR_INVALID_COMBINATION_OF_OPTIONS -403
+
+/* errors related to thread */
+/* #define ONIGERR_OVER_THREAD_PASS_LIMIT_COUNT -1001 */
+
+
+/* must be smaller than BIT_STATUS_BITS_NUM (unsigned int * 8) */
+#define ONIG_MAX_CAPTURE_HISTORY_GROUP 31
+#define ONIG_IS_CAPTURE_HISTORY_GROUP(r, i) \
+ ((i) <= ONIG_MAX_CAPTURE_HISTORY_GROUP && (r)->list && (r)->list[i])
+
+#ifdef USE_CAPTURE_HISTORY
+typedef struct OnigCaptureTreeNodeStruct {
+ int group; /* group number */
+ OnigPosition beg;
+ OnigPosition end;
+ int allocated;
+ int num_childs;
+ struct OnigCaptureTreeNodeStruct** childs;
+} OnigCaptureTreeNode;
+#endif
+
+/* match result region type */
+struct re_registers {
+ int allocated;
+ int num_regs;
+ OnigPosition* beg;
+ OnigPosition* end;
+#ifdef USE_CAPTURE_HISTORY
+ /* extended */
+ OnigCaptureTreeNode* history_root; /* capture history tree root */
+#endif
+};
+
+/* capture tree traverse */
+#define ONIG_TRAVERSE_CALLBACK_AT_FIRST 1
+#define ONIG_TRAVERSE_CALLBACK_AT_LAST 2
+#define ONIG_TRAVERSE_CALLBACK_AT_BOTH \
+ ( ONIG_TRAVERSE_CALLBACK_AT_FIRST | ONIG_TRAVERSE_CALLBACK_AT_LAST )
+
+
+#define ONIG_REGION_NOTPOS -1
+
+typedef struct re_registers OnigRegion;
+
+typedef struct {
+ OnigEncoding enc;
+ OnigUChar* par;
+ OnigUChar* par_end;
+} OnigErrorInfo;
+
+typedef struct {
+ int lower;
+ int upper;
+} OnigRepeatRange;
+
+typedef void (*OnigWarnFunc)(const char* s);
+extern void onig_null_warn(const char* s);
+#define ONIG_NULL_WARN onig_null_warn
+
+#define ONIG_CHAR_TABLE_SIZE 256
+
+typedef struct re_pattern_buffer {
+ /* common members of BBuf(bytes-buffer) */
+ unsigned char* p; /* compiled pattern */
+ unsigned int used; /* used space for p */
+ unsigned int alloc; /* allocated space for p */
+
+ int num_mem; /* used memory(...) num counted from 1 */
+ int num_repeat; /* OP_REPEAT/OP_REPEAT_NG id-counter */
+ int num_null_check; /* OP_NULL_CHECK_START/END id counter */
+ int num_comb_exp_check; /* combination explosion check */
+ int num_call; /* number of subexp call */
+ unsigned int capture_history; /* (?@...) flag (1-31) */
+ unsigned int bt_mem_start; /* need backtrack flag */
+ unsigned int bt_mem_end; /* need backtrack flag */
+ int stack_pop_level;
+ int repeat_range_alloc;
+
+ OnigOptionType options;
+
+ OnigRepeatRange* repeat_range;
+
+ OnigEncoding enc;
+ const OnigSyntaxType* syntax;
+ void* name_table;
+ OnigCaseFoldType case_fold_flag;
+
+ /* optimization info (string search, char-map and anchors) */
+ int optimize; /* optimize flag */
+ int threshold_len; /* search str-length for apply optimize */
+ int anchor; /* BEGIN_BUF, BEGIN_POS, (SEMI_)END_BUF */
+ OnigDistance anchor_dmin; /* (SEMI_)END_BUF anchor distance */
+ OnigDistance anchor_dmax; /* (SEMI_)END_BUF anchor distance */
+ int sub_anchor; /* start-anchor for exact or map */
+ unsigned char *exact;
+ unsigned char *exact_end;
+ unsigned char map[ONIG_CHAR_TABLE_SIZE]; /* used as BM skip or char-map */
+ int *reserved1;
+ int *reserved2;
+ OnigDistance dmin; /* min-distance of exact or map */
+ OnigDistance dmax; /* max-distance of exact or map */
+
+ /* rb_hrtime_t from hrtime.h */
+#ifdef MY_RUBY_BUILD_MAY_TIME_TRAVEL
+ int128_t timelimit;
+#else
+ uint64_t timelimit;
+#endif
+
+ /* regex_t link chain */
+ struct re_pattern_buffer* chain; /* escape compile-conflict */
+} OnigRegexType;
+
+typedef OnigRegexType* OnigRegex;
+
+#ifndef ONIG_ESCAPE_REGEX_T_COLLISION
+typedef OnigRegexType regex_t;
+#endif
+
+
+typedef struct {
+ int num_of_elements;
+ OnigEncoding pattern_enc;
+ OnigEncoding target_enc;
+ const OnigSyntaxType* syntax;
+ OnigOptionType option;
+ OnigCaseFoldType case_fold_flag;
+} OnigCompileInfo;
+
+/* Oniguruma Native API */
+ONIG_EXTERN
+int onig_initialize(OnigEncoding encodings[], int n);
+ONIG_EXTERN
+int onig_init(void);
+ONIG_EXTERN
+int onig_error_code_to_str(OnigUChar* s, OnigPosition err_code, ...);
+ONIG_EXTERN
+void onig_set_warn_func(OnigWarnFunc f);
+ONIG_EXTERN
+void onig_set_verb_warn_func(OnigWarnFunc f);
+ONIG_EXTERN
+int onig_new(OnigRegex*, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax, OnigErrorInfo* einfo);
+ONIG_EXTERN
+int onig_reg_init(OnigRegex reg, OnigOptionType option, OnigCaseFoldType case_fold_flag, OnigEncoding enc, const OnigSyntaxType* syntax);
+ONIG_EXTERN
+int onig_new_without_alloc(OnigRegex, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax, OnigErrorInfo* einfo);
+ONIG_EXTERN
+int onig_new_deluxe(OnigRegex* reg, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigCompileInfo* ci, OnigErrorInfo* einfo);
+ONIG_EXTERN
+void onig_free(OnigRegex);
+ONIG_EXTERN
+void onig_free_body(OnigRegex);
+ONIG_EXTERN
+int onig_reg_copy(OnigRegex* reg, OnigRegex orig_reg);
+ONIG_EXTERN
+OnigPosition onig_scan(OnigRegex reg, const OnigUChar* str, const OnigUChar* end, OnigRegion* region, OnigOptionType option, int (*scan_callback)(OnigPosition, OnigPosition, OnigRegion*, void*), void* callback_arg);
+ONIG_EXTERN
+OnigPosition onig_search(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option);
+ONIG_EXTERN
+OnigPosition onig_search_gpos(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* global_pos, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option);
+ONIG_EXTERN
+OnigPosition onig_match(OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* at, OnigRegion* region, OnigOptionType option);
+ONIG_EXTERN
+int onig_check_linear_time(OnigRegex reg);
+ONIG_EXTERN
+OnigRegion* onig_region_new(void);
+ONIG_EXTERN
+void onig_region_init(OnigRegion* region);
+ONIG_EXTERN
+void onig_region_free(OnigRegion* region, int free_self);
+ONIG_EXTERN
+void onig_region_copy(OnigRegion* to, const OnigRegion* from);
+ONIG_EXTERN
+void onig_region_clear(OnigRegion* region);
+ONIG_EXTERN
+int onig_region_resize(OnigRegion* region, int n);
+ONIG_EXTERN
+int onig_region_set(OnigRegion* region, int at, int beg, int end);
+ONIG_EXTERN
+int onig_name_to_group_numbers(OnigRegex reg, const OnigUChar* name, const OnigUChar* name_end, int** nums);
+ONIG_EXTERN
+int onig_name_to_backref_number(OnigRegex reg, const OnigUChar* name, const OnigUChar* name_end, const OnigRegion *region);
+ONIG_EXTERN
+int onig_foreach_name(OnigRegex reg, int (*func)(const OnigUChar*, const OnigUChar*,int,int*,OnigRegex,void*), void* arg);
+ONIG_EXTERN
+int onig_number_of_names(const OnigRegexType *reg);
+ONIG_EXTERN
+int onig_number_of_captures(const OnigRegexType *reg);
+ONIG_EXTERN
+int onig_number_of_capture_histories(const OnigRegexType *reg);
+#ifdef USE_CAPTURE_HISTORY
+ONIG_EXTERN
+OnigCaptureTreeNode* onig_get_capture_tree(OnigRegion* region);
+#endif
+ONIG_EXTERN
+int onig_capture_tree_traverse(OnigRegion* region, int at, int(*callback_func)(int,OnigPosition,OnigPosition,int,int,void*), void* arg);
+ONIG_EXTERN
+int onig_noname_group_capture_is_active(const OnigRegexType *reg);
+ONIG_EXTERN
+OnigEncoding onig_get_encoding(const OnigRegexType *reg);
+ONIG_EXTERN
+OnigOptionType onig_get_options(const OnigRegexType *reg);
+ONIG_EXTERN
+OnigCaseFoldType onig_get_case_fold_flag(const OnigRegexType *reg);
+ONIG_EXTERN
+const OnigSyntaxType* onig_get_syntax(const OnigRegexType *reg);
+ONIG_EXTERN
+int onig_set_default_syntax(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+void onig_copy_syntax(OnigSyntaxType* to, const OnigSyntaxType* from);
+ONIG_EXTERN
+unsigned int onig_get_syntax_op(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+unsigned int onig_get_syntax_op2(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+unsigned int onig_get_syntax_behavior(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+OnigOptionType onig_get_syntax_options(const OnigSyntaxType* syntax);
+ONIG_EXTERN
+void onig_set_syntax_op(OnigSyntaxType* syntax, unsigned int op);
+ONIG_EXTERN
+void onig_set_syntax_op2(OnigSyntaxType* syntax, unsigned int op2);
+ONIG_EXTERN
+void onig_set_syntax_behavior(OnigSyntaxType* syntax, unsigned int behavior);
+ONIG_EXTERN
+void onig_set_syntax_options(OnigSyntaxType* syntax, OnigOptionType options);
+ONIG_EXTERN
+int onig_set_meta_char(OnigSyntaxType* syntax, unsigned int what, OnigCodePoint code);
+ONIG_EXTERN
+void onig_copy_encoding(OnigEncodingType *to, OnigEncoding from);
+ONIG_EXTERN
+OnigCaseFoldType onig_get_default_case_fold_flag(void);
+ONIG_EXTERN
+int onig_set_default_case_fold_flag(OnigCaseFoldType case_fold_flag);
+ONIG_EXTERN
+unsigned int onig_get_match_stack_limit_size(void);
+ONIG_EXTERN
+int onig_set_match_stack_limit_size(unsigned int size);
+ONIG_EXTERN
+unsigned int onig_get_parse_depth_limit(void);
+ONIG_EXTERN
+int onig_set_parse_depth_limit(unsigned int depth);
+ONIG_EXTERN
+int onig_end(void);
+ONIG_EXTERN
+const char* onig_version(void);
+ONIG_EXTERN
+const char* onig_copyright(void);
+
+RUBY_SYMBOL_EXPORT_END
+
+#ifdef __cplusplus
+# if 0
+{ /* satisfy cc-mode */
+# endif
+}
+#endif
+
+#endif /* ONIGMO_H */
diff --git a/include/ruby/oniguruma.h b/include/ruby/oniguruma.h
new file mode 100644
index 0000000000..dc83754aca
--- /dev/null
+++ b/include/ruby/oniguruma.h
@@ -0,0 +1,8 @@
+#ifndef ONIGURUMA_H
+#define ONIGURUMA_H
+#include "onigmo.h"
+#define ONIGURUMA
+#define ONIGURUMA_VERSION_MAJOR ONIGMO_VERSION_MAJOR
+#define ONIGURUMA_VERSION_MINOR ONIGMO_VERSION_MINOR
+#define ONIGURUMA_VERSION_TEENY ONIGMO_VERSION_TEENY
+#endif /* ONIGURUMA_H */
diff --git a/include/ruby/ractor.h b/include/ruby/ractor.h
new file mode 100644
index 0000000000..8cfca21621
--- /dev/null
+++ b/include/ruby/ractor.h
@@ -0,0 +1,278 @@
+#ifndef RUBY_RACTOR_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_RACTOR_H 1
+
+/**
+ * @file
+ * @author Koichi Sasada
+ * @date Tue Nov 17 16:39:15 2020
+ * @copyright Copyright (C) 2020 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#include "internal/dllexport.h" /* RUBY_EXTERN is here */
+#include "internal/fl_type.h" /* FL_TEST_RAW is here */
+#include "internal/special_consts.h" /* RB_SPECIAL_CONSTS_P is here */
+#include "internal/stdbool.h" /* bool is here */
+#include "internal/value.h" /* VALUE is here */
+
+/** Type that defines a ractor-local storage. */
+struct rb_ractor_local_storage_type {
+
+ /**
+ * A function to mark a ractor-local storage.
+ *
+ * @param[out] ptr A ractor-local storage.
+ * @post Ruby objects inside of `ptr` are marked.
+ */
+ void (*mark)(void *ptr);
+
+ /**
+ * A function to destruct a ractor-local storage.
+ *
+ * @param[out] ptr A ractor-local storage.
+ * @post `ptr` is not a valid pointer.
+ */
+ void (*free)(void *ptr);
+ // TODO: update
+};
+
+/** (Opaque) struct that holds a ractor-local storage key. */
+typedef struct rb_ractor_local_key_struct *rb_ractor_local_key_t;
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * `Ractor` class.
+ *
+ * @ingroup object
+ */
+RUBY_EXTERN VALUE rb_cRactor;
+
+/**
+ * Queries the standard input of the current Ractor that is calling this
+ * function.
+ *
+ * @return An IO.
+ * @note This can be different from the process-global one.
+ */
+VALUE rb_ractor_stdin(void);
+
+/**
+ * Queries the standard output of the current Ractor that is calling this
+ * function.
+ *
+ * @return An IO.
+ * @note This can be different from the process-global one.
+ */
+VALUE rb_ractor_stdout(void);
+
+/**
+ * Queries the standard error of the current Ractor that is calling this
+ * function.
+ *
+ * @return An IO.
+ * @note This can be different from the process-global one.
+ */
+VALUE rb_ractor_stderr(void);
+
+/**
+ * Assigns an IO to the standard input of the Ractor that is calling this
+ * function.
+ *
+ * @param[in] io An IO.
+ * @post `io` is the standard input of the current ractor.
+ * @post In case the calling Ractor is the main Ractor, it also updates
+ * the process global ::rb_stdin.
+ */
+void rb_ractor_stdin_set(VALUE io);
+
+/**
+ * Assigns an IO to the standard output of the Ractor that is calling this
+ * function.
+ *
+ * @param[in] io An IO.
+ * @post `io` is the standard input of the current ractor.
+ * @post In case the calling Ractor is the main Ractor, it also updates
+ * the process global ::rb_stdout.
+ */
+void rb_ractor_stdout_set(VALUE io);
+
+/**
+ * Assigns an IO to the standard error of the Ractor that is calling this
+ * function.
+ *
+ * @param[in] io An IO.
+ * @post `io` is the standard input of the current ractor.
+ * @post In case the calling Ractor is the main Ractor, it also updates
+ * the process global ::rb_stderr.
+ */
+void rb_ractor_stderr_set(VALUE io);
+
+/**
+ * Issues a new key.
+ *
+ * @return A newly issued ractor-local storage key. Keys issued using this
+ * key can be associated to a Ruby object per Ractor.
+ */
+rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void);
+
+/**
+ * Queries the key.
+ *
+ * @param[in] key A ractor-local storage key to lookup.
+ * @retval RUBY_Qnil No such key.
+ * @retval otherwise A value corresponds to `key` in the current Ractor.
+ * @note This cannot distinguish between a nonexistent key and a key
+ * exists and corresponds to ::RUBY_Qnil.
+ */
+VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key);
+
+/**
+ * Queries the key.
+ *
+ * @param[in] key A ractor-local storage key to lookup.
+ * @param[out] val Return value buffer.
+ * @retval false `key` not found.
+ * @retval true `key` found.
+ * @post `val` is updated so that it has the value corresponds to `key`
+ * in the current Ractor.
+ */
+bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val);
+
+/**
+ * Associates the passed value to the passed key.
+ *
+ * @param[in] key A ractor-local storage key.
+ * @param[in] val Arbitrary ruby object.
+ * @post `val` corresponds to `key` in the current Ractor.
+ */
+void rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val);
+
+/**
+ * A type of ractor-local storage that destructs itself using ::ruby_xfree.
+ *
+ * @internal
+ *
+ * Why it is visible from 3rd party extension libraries is not obvious to
+ * @shyouhei.
+ */
+RUBY_EXTERN const struct rb_ractor_local_storage_type rb_ractor_local_storage_type_free;
+
+/** @alias{rb_ractor_local_storage_type_free} */
+#define RB_RACTOR_LOCAL_STORAGE_TYPE_FREE (&rb_ractor_local_storage_type_free)
+
+/**
+ * Extended version of rb_ractor_local_storage_value_newkey(). It additionally
+ * takes the type of the issuing key.
+ *
+ * @param[in] type How the value associated with the issuing key should
+ * behave.
+ * @return A newly issued ractor-local storage key, of type `type`.
+ */
+rb_ractor_local_key_t rb_ractor_local_storage_ptr_newkey(const struct rb_ractor_local_storage_type *type);
+
+/**
+ * Identical to rb_ractor_local_storage_value() except the return type.
+ *
+ * @param[in] key A ractor-local storage key to lookup.
+ * @retval NULL No such key.
+ * @retval otherwise A value corresponds to `key` in the current Ractor.
+ */
+void *rb_ractor_local_storage_ptr(rb_ractor_local_key_t key);
+
+/**
+ * Identical to rb_ractor_local_storage_value_set() except the parameter type.
+ *
+ * @param[in] key A ractor-local storage key.
+ * @param[in] ptr A pointer that conforms `key`'s type.
+ * @post `ptr` corresponds to `key` in the current Ractor.
+ */
+void rb_ractor_local_storage_ptr_set(rb_ractor_local_key_t key, void *ptr);
+
+/**
+ * Destructively transforms the passed object so that multiple Ractors can
+ * share it. What is a shareable object and what is not is a nuanced concept,
+ * and @ko1 says the definition can still change. However extension library
+ * authors might interest to learn how to use #RUBY_TYPED_FROZEN_SHAREABLE.
+ *
+ * @param[out] obj Arbitrary ruby object to modify.
+ * @exception rb_eRactorError Ractors cannot share `obj` by nature.
+ * @return Passed `obj`.
+ * @post Multiple Ractors can share `obj`.
+ *
+ * @internal
+ *
+ * In case an exception is raised, `obj` remains in an intermediate state where
+ * some of its part is frozen and others are not. @shyouhei is not sure if it
+ * is either an intended behaviour, current implementation limitation, or
+ * simply a bug. Note also that there is no way to "melt" a frozen object.
+ */
+VALUE rb_ractor_make_shareable(VALUE obj);
+
+/**
+ * Identical to rb_ractor_make_shareable(), except it returns a (deep) copy of
+ * the passed one instead of modifying it in-place.
+ *
+ * @param[in] obj Arbitrary ruby object to duplicate.
+ * @exception rb_eRactorError Ractors cannot share `obj` by nature.
+ * @return A deep copy of `obj` which is shareable among Ractors.
+ */
+VALUE rb_ractor_make_shareable_copy(VALUE obj);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+/**
+ * Queries if the passed object has previously classified as shareable or not.
+ * This doesn't mean anything in practice... Objects can be shared later.
+ * Always use rb_ractor_shareable_p() instead.
+ *
+ * @param[in] obj Object in question.
+ * @retval RUBY_FL_SHAREABLE It once was shareable before.
+ * @retval 0 Otherwise.
+ */
+#define RB_OBJ_SHAREABLE_P(obj) FL_TEST_RAW((obj), RUBY_FL_SHAREABLE)
+
+/**
+ * Queries if multiple Ractors can share the passed object or not. Ractors run
+ * without protecting each other. Sharing an object among them is basically
+ * dangerous, disabled by default. However there are objects that are
+ * extremely carefully implemented to be Ractor-safe; for instance integers
+ * have such property. This function can classify that.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @retval true `obj` is capable of shared across ractors.
+ * @retval false `obj` cannot travel across ractor boundaries.
+ */
+static inline bool
+rb_ractor_shareable_p(VALUE obj)
+{
+ bool rb_ractor_shareable_p_continue(VALUE obj);
+
+ if (RB_SPECIAL_CONST_P(obj)) {
+ return true;
+ }
+ else if (RB_OBJ_SHAREABLE_P(obj)) {
+ return true;
+ }
+ else {
+ return rb_ractor_shareable_p_continue(obj);
+ }
+}
+
+// TODO: optimize on interpreter core
+#ifndef RB_OBJ_SET_SHAREABLE
+VALUE rb_obj_set_shareable(VALUE obj); // ractor.c
+#define RB_OBJ_SET_SHAREABLE(obj) rb_obj_set_shareable(obj)
+#endif
+
+static inline VALUE
+RB_OBJ_SET_FROZEN_SHAREABLE(VALUE obj)
+{
+ RB_OBJ_FREEZE(obj);
+ RB_OBJ_SET_SHAREABLE(obj);
+ return obj;
+}
+
+#endif /* RUBY_RACTOR_H */
diff --git a/include/ruby/random.h b/include/ruby/random.h
new file mode 100644
index 0000000000..740be6bdad
--- /dev/null
+++ b/include/ruby/random.h
@@ -0,0 +1,361 @@
+#ifndef RUBY_RANDOM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_RANDOM_H 1
+/**
+ * @file
+ * @date Sat May 7 11:51:14 JST 2016
+ * @copyright 2007-2020 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ *
+ * This is a set of APIs to roll your own subclass of ::rb_cRandom. An
+ * illustrative example of such PRNG can be found at
+ * `ext/-test-/random/loop.c`.
+ */
+
+#include "ruby/ruby.h"
+
+/*
+ * version
+ * 0: before versioning; deprecated
+ * 1: added version, flags and init_32bit function
+ */
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR 1
+#define RUBY_RANDOM_INTERFACE_VERSION_MINOR 0
+
+#define RUBY_RANDOM_PASTE_VERSION_SUFFIX(x, y, z) x##_##y##_##z
+#define RUBY_RANDOM_WITH_VERSION_SUFFIX(name, major, minor) \
+ RUBY_RANDOM_PASTE_VERSION_SUFFIX(name, major, minor)
+#define rb_random_data_type \
+ RUBY_RANDOM_WITH_VERSION_SUFFIX(rb_random_data_type, \
+ RUBY_RANDOM_INTERFACE_VERSION_MAJOR, \
+ RUBY_RANDOM_INTERFACE_VERSION_MINOR)
+#define RUBY_RANDOM_INTERFACE_VERSION_INITIALIZER \
+ {RUBY_RANDOM_INTERFACE_VERSION_MAJOR, RUBY_RANDOM_INTERFACE_VERSION_MINOR}
+#define RUBY_RANDOM_INTERFACE_VERSION_MAJOR_MAX 0xff
+#define RUBY_RANDOM_INTERFACE_VERSION_MINOR_MAX 0xff
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Base components of the random interface.
+ *
+ * @internal
+ *
+ * Ideally this could be an empty class if we could assume C++, but in C a
+ * struct must have at least one field.
+ */
+struct rb_random_struct {
+ /** Seed, passed through e.g. `Random.new` */
+ VALUE seed;
+};
+typedef struct rb_random_struct rb_random_t; /**< @see ::rb_random_struct */
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is the type of functions called when your random object is initialised.
+ * Passed buffer is the seed object basically. But in Ruby a number can be
+ * really big. This type of functions accept such big integers as a series of
+ * machine words.
+ *
+ * @param[out] rng Your random struct to fill in.
+ * @param[in] buf Seed, maybe converted from a bignum.
+ * @param[in] len Number of words of `buf`.
+ * @post `rng` is initialised using the passed seeds.
+ */
+typedef void rb_random_init_func(rb_random_t *rng, const uint32_t *buf, size_t len);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is the type of functions called when your random object is initialised.
+ * Passed data is the seed integer.
+ *
+ * @param[out] rng Your random struct to fill in.
+ * @param[in] data Seed, single word.
+ * @post `rng` is initialised using the passed seeds.
+ */
+typedef void rb_random_init_int32_func(rb_random_t *rng, uint32_t data);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is the type of functions called from your object's `#rand` method.
+ *
+ * @param[out] rng Your random struct to extract an integer from.
+ * @return A random number.
+ * @post `rng` is consumed somehow.
+ */
+typedef unsigned int rb_random_get_int32_func(rb_random_t *rng);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is the type of functions called from your object's `#bytes` method.
+ *
+ * @param[out] rng Your random struct to extract an integer from.
+ * @param[out] buf Return buffer of at least `len` bytes length.
+ * @param[in] len Number of bytes of `buf`.
+ * @post `rng` is consumed somehow.
+ * @post `buf` is filled with random bytes.
+ */
+typedef void rb_random_get_bytes_func(rb_random_t *rng, void *buf, size_t len);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is the type of functions called from your object's `#rand` method.
+ *
+ * @param[out] rng Your random struct to extract an integer from.
+ * @param[in] excl Pass nonzero value here to indicate you don't want 1.0.
+ * @return A random number of range 0.0 to 1.0.
+ * @post `rng` is consumed somehow.
+ */
+typedef double rb_random_get_real_func(rb_random_t *rng, int excl);
+
+/** PRNG algorithmic interface, analogous to Ruby level classes. */
+typedef struct {
+ /** Number of bits of seed numbers. */
+ size_t default_seed_bits;
+
+ /**
+ * Major/minor versions of this interface
+ */
+ struct {
+ uint8_t major, minor;
+ } version;
+
+ /**
+ * Reserved flags
+ */
+ uint16_t flags;
+
+ /** Function to initialize from uint32_t array. */
+ rb_random_init_func *init;
+
+ /** Function to initialize from single uint32_t. */
+ rb_random_init_int32_func *init_int32;
+
+ /** Function to obtain a random integer. */
+ rb_random_get_int32_func *get_int32;
+
+ /**
+ * Function to obtain a series of random bytes. If your PRNG have a native
+ * method to yield arbitrary number of bytes use that to implement this.
+ * But in case you lack such things, you can do so by using
+ * rb_rand_bytes_int32()
+ *
+ * ```CXX
+ * extern rb_random_get_int32_func your_get_int32_func;
+ *
+ * void
+ * your_get_byes_func(rb_random_t *rng, void *buf, size_t len)
+ * {
+ * rb_rand_bytes_int32(your_get_int32_func, rng, buf, len);
+ * }
+ * ```
+ */
+ rb_random_get_bytes_func *get_bytes;
+
+ /**
+ * Function to obtain a random double. If your PRNG have a native method
+ * to yield a floating point random number use that to implement this. But
+ * in case you lack such things, you can do so by using
+ * rb_int_pair_to_real().
+ *
+ * ```CXX
+ * extern rb_random_get_int32_func your_get_int32_func;
+ *
+ * void
+ * your_get_real_func(rb_random_t *rng, int excl)
+ * {
+ * auto a = your_get_int32_func(rng);
+ * auto b = your_get_int32_func(rng);
+ * return rb_int_pair_to_real(a, b, excl);
+ * }
+ * ```
+ */
+ rb_random_get_real_func *get_real;
+} rb_random_interface_t;
+
+/**
+ * This utility macro defines 4 functions named prefix_init, prefix_init_int32,
+ * prefix_get_int32, prefix_get_bytes.
+ */
+#define RB_RANDOM_INTERFACE_DECLARE(prefix) \
+ static void prefix##_init(rb_random_t *, const uint32_t *, size_t); \
+ static void prefix##_init_int32(rb_random_t *, uint32_t); \
+ static unsigned int prefix##_get_int32(rb_random_t *); \
+ static void prefix##_get_bytes(rb_random_t *, void *, size_t)
+
+/**
+ * Identical to #RB_RANDOM_INTERFACE_DECLARE except it also declares
+ * prefix_get_real.
+ */
+#define RB_RANDOM_INTERFACE_DECLARE_WITH_REAL(prefix) \
+ RB_RANDOM_INTERFACE_DECLARE(prefix); \
+ static double prefix##_get_real(rb_random_t *, int)
+
+/**
+ * This utility macro expands to the names declared using
+ * #RB_RANDOM_INTERFACE_DECLARE. Expected to be used inside of a
+ * ::rb_random_interface_t initialiser:
+ *
+ * ```CXX
+ * RB_RANDOM_INTERFACE_DECLARE(foo);
+ *
+ * static inline constexpr rb_random_interface_t foo_interface = {
+ * 32768, // bits
+ * RB_RANDOM_INTERFACE_DEFINE(foo),
+ * };
+ * ```
+ */
+#define RB_RANDOM_INTERFACE_DEFINE(prefix) \
+ RUBY_RANDOM_INTERFACE_VERSION_INITIALIZER, 0, \
+ prefix##_init, \
+ prefix##_init_int32, \
+ prefix##_get_int32, \
+ prefix##_get_bytes
+
+/**
+ * Identical to #RB_RANDOM_INTERFACE_DEFINE except it also defines
+ * prefix_get_real.
+ */
+#define RB_RANDOM_INTERFACE_DEFINE_WITH_REAL(prefix) \
+ RB_RANDOM_INTERFACE_DEFINE(prefix), \
+ prefix##_get_real
+
+#define RB_RANDOM_DEFINE_INIT_INT32_FUNC(prefix) \
+ static void prefix##_init_int32(rb_random_t *rnd, uint32_t data) \
+ { \
+ prefix##_init(rnd, &data, 1); \
+ }
+
+#if defined _WIN32 && !defined __CYGWIN__
+typedef rb_data_type_t rb_random_data_type_t;
+# define RB_RANDOM_PARENT 0
+#else
+
+/** This is the type of ::rb_random_data_type. */
+typedef const rb_data_type_t rb_random_data_type_t;
+
+/**
+ * This utility macro can be used when you define your own PRNG type:
+ *
+ * ```CXX
+ * static inline constexpr rb_random_interface_t your_if = {
+ * 0, RB_RANDOM_INTERFACE_DEFINE(your),
+ * };
+ *
+ * static inline constexpr rb_random_data_type_t your_prng_type = {
+ * "your PRNG",
+ * { rb_random_mark, },
+ * RB_RANDOM_PARENT, // <<-- HERE
+ * &your_if,
+ * 0,
+ * }
+ * ```
+ */
+# define RB_RANDOM_PARENT &rb_random_data_type
+#endif
+
+/**
+ * This macro is expected to be called exactly once at the beginning of a
+ * program, possibly from inside of your `Init_Foo()` function. Depending on
+ * platforms #RB_RANDOM_PARENT can require a fixup. This routine does that
+ * when necessary.
+ */
+#define RB_RANDOM_DATA_INIT_PARENT(random_data) \
+ rbimpl_random_data_init_parent(&random_data)
+
+/**
+ * This is the implementation of ::rb_data_type_struct::dmark for
+ * ::rb_random_data_type. In case your PRNG does not involve Ruby objects at
+ * all (which is quite likely), you can simply reuse it.
+ *
+ * @param[out] ptr Target to mark, which is a ::rb_random_t this case.
+ */
+void rb_random_mark(void *ptr);
+
+/**
+ * Initialises an allocated ::rb_random_t instance. Call it from your own
+ * initialiser appropriately.
+ *
+ * @param[out] rnd Your PRNG's base part.
+ * @post `rnd` is filled with an initial state.
+ */
+void rb_random_base_init(rb_random_t *rnd);
+
+/**
+ * Generates a 64 bit floating point number by concatenating two 32bit unsigned
+ * integers.
+ *
+ * @param[in] a Most significant 32 bits of the result.
+ * @param[in] b Least significant 32 bits of the result.
+ * @param[in] excl Whether the result should exclude 1.0 or not.
+ * @return A double, whose range is either `[0, 1)` or `[0, 1]`.
+ * @see ::rb_random_interface_t::get_real()
+ *
+ * @internal
+ *
+ * This in fact has nothing to do with PRNGs.
+ */
+double rb_int_pair_to_real(uint32_t a, uint32_t b, int excl);
+
+/**
+ * Repeatedly calls the passed function over and over again until the passed
+ * buffer is filled with random bytes.
+ *
+ * @param[in] func Generator function.
+ * @param[out] prng Passed as-is to `func`.
+ * @param[out] buff Return buffer.
+ * @param[in] size Number of words of `buff`.
+ * @post `buff` is filled with random bytes.
+ * @post `prng` is updated by `func`.
+ * @see ::rb_random_interface_t::get_bytes()
+ */
+void rb_rand_bytes_int32(rb_random_get_int32_func *func, rb_random_t *prng, void *buff, size_t size);
+
+/**
+ * The data that holds the backend type of ::rb_cRandom. Used as your PRNG's
+ * ::rb_data_type_struct::parent.
+ */
+RUBY_EXTERN const rb_data_type_t rb_random_data_type;
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */
+/**
+ * Queries the interface of the passed random object.
+ *
+ * @param[in] obj An instance (of a subclass) of ::rb_cRandom.
+ * @return Its corresponding ::rb_random_interface_t interface.
+ */
+static inline const rb_random_interface_t *
+rb_rand_if(VALUE obj)
+{
+ RBIMPL_ASSERT_OR_ASSUME(RB_TYPE_P(obj, T_DATA));
+ RBIMPL_ASSERT_OR_ASSUME(RTYPEDDATA_P(obj));
+ RUBY_ASSERT(rb_typeddata_is_kind_of(obj, &rb_random_data_type));
+ const struct rb_data_type_struct *t = RTYPEDDATA_TYPE(obj);
+ const void *ret = t->data;
+ return RBIMPL_CAST((const rb_random_interface_t *)ret);
+}
+
+RBIMPL_ATTR_NOALIAS()
+/**
+ * @private
+ *
+ * This is an implementation detail of #RB_RANDOM_DATA_INIT_PARENT. People
+ * don't use it directly.
+ *
+ * @param[out] random_data Region to fill.
+ * @post ::rb_random_data_type is filled appropriately.
+ */
+static inline void
+rbimpl_random_data_init_parent(rb_random_data_type_t *random_data)
+{
+#if defined _WIN32 && !defined __CYGWIN__
+ random_data->parent = &rb_random_data_type;
+#endif
+}
+
+#endif /* RUBY_RANDOM_H */
diff --git a/include/ruby/re.h b/include/ruby/re.h
new file mode 100644
index 0000000000..f86d6f26cf
--- /dev/null
+++ b/include/ruby/re.h
@@ -0,0 +1,172 @@
+#ifndef RUBY_RE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_RE_H 1
+/**
+ * @file
+ * @author $Author$
+ * @date Thu Sep 30 14:18:32 JST 1993
+ * @copyright Copyright (C) 1993-2007 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#include "ruby/internal/config.h"
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <stdio.h>
+
+#include "ruby/onigmo.h"
+#include "ruby/regex.h"
+#include "ruby/internal/core/rmatch.h"
+#include "ruby/internal/dllexport.h"
+
+struct re_registers; /* Defined in onigmo.h */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Creates a new instance of ::rb_cRegexp. It can be seen as a specialised
+ * version of rb_reg_new_str() where it does not take options.
+ *
+ * @param[in] str Source code in String.
+ * @return Allocated new instance of ::rb_cRegexp.
+ */
+VALUE rb_reg_regcomp(VALUE str);
+
+/**
+ * Runs the passed regular expression over the passed string. Unlike
+ * rb_reg_search() this function also takes position and direction of the
+ * search, which make it possible for this function to run from in middle of
+ * the string.
+ *
+ * @param[in] re Regular expression to execute.
+ * @param[in] str Target string to search.
+ * @param[in] pos Offset in `str` to start searching, in bytes.
+ * @param[in] dir `pos`' direction; 0 means left-to-right, 1 for
+ * the opposite.
+ * @exception rb_eArgError `re` is broken.
+ * @exception rb_eRegexpError `re` is malformed.
+ * @retval -1 Match failed.
+ * @retval otherwise Offset of first such byte where match happened.
+ * @post `Regexp.last_match` is updated.
+ * @post `$&`, `$~`, etc., are updated.
+ *
+ * @internal
+ *
+ * Distinction between raising ::rb_eArgError and ::rb_eRegexpError is not
+ * obvious, at least to @shyouhei.
+ */
+long rb_reg_search(VALUE re, VALUE str, long pos, int dir);
+
+/**
+ * Substitution. This is basically the implementation of `String#sub`. Also
+ * `String#gsub` repeatedly calls this function.
+ *
+ * @param[in] repl Replacement string, e.g. `"\\1\\2"`
+ * @param[in] src Source string, to be replaced.
+ * @param[in] regs Matched data generated by applying `rexp` to `src`.
+ * @param[in] rexp Regular expression.
+ * @return A substituted string.
+ *
+ * @internal
+ *
+ * This function does not check for encoding compatibility. `String#sub!`
+ * etc. employ their own checker.
+ *
+ * `regs` should have been `const struct re_registers *` because it is read
+ * only. Kept as-is for compatibility.
+ */
+VALUE rb_reg_regsub(VALUE repl, VALUE src, struct re_registers *regs, VALUE rexp);
+
+/**
+ * Tell us if this is a wrong idea, but it seems this function has no usage at
+ * all. Just remains here for theoretical backwards compatibility.
+ *
+ * @param[in] re Regular expression to execute.
+ * @param[in] str Target string to search.
+ * @param[in] pos Offset in `str` to start searching, in bytes.
+ * @param[in] dir `pos`' direction; 0 means left-to-right, 1 for
+ * the opposite.
+ * @return Adjusted nearest offset to `pos` inside of `str`, where is a
+ * character boundary.
+ *
+ */
+long rb_reg_adjust_startpos(VALUE re, VALUE str, long pos, int dir);
+
+/**
+ * Escapes any characters that would have special meaning in a regular
+ * expression.
+ *
+ * @param[in] str Target string to escape.
+ * @return A copy of `str` whose contents are escaped.
+ */
+VALUE rb_reg_quote(VALUE str);
+
+/**
+ * Exercises various checks and preprocesses so that the given regular
+ * expression can be applied to the given string. The preprocess here includes
+ * (but not limited to) for instance encoding conversion.
+ *
+ * @param[in] re Target regular expression.
+ * @param[in] str What `re` is about to run on.
+ * @exception rb_eArgError `re` does not fit for `str`.
+ * @exception rb_eEncCompatError `re` and `str` are incompatible.
+ * @exception rb_eRegexpError `re` is malformed.
+ * @return A preprocessesed pattern buffer ready to be applied to `str`.
+ * @note The return value is manages by our GC. Don't free.
+ *
+ * @internal
+ *
+ * The return type, `regex_t *`, is defined in `<ruby/onigmo.h>`, _and_
+ * _conflicts_ with POSIX's `<regex.h>`. We can no longer save the situation
+ * at this point. Just don't mix the two.
+ */
+regex_t *rb_reg_prepare_re(VALUE re, VALUE str);
+
+/**
+ * Runs a regular expression match using function `match`. Performs preparation,
+ * error handling, and memory cleanup.
+ *
+ * @param[in] re Target regular expression.
+ * @param[in] str What `re` is about to run on.
+ * @param[in] match The function to run to match `str` against `re`.
+ * @param[in] args Pointer to arguments to pass into `match`.
+ * @param[out] regs Registers on a successful match.
+ * @exception rb_eArgError `re` does not fit for `str`.
+ * @exception rb_eEncCompatError `re` and `str` are incompatible.
+ * @exception rb_eRegexpError `re` is malformed.
+ * @return Match position on a successful match, `ONIG_MISMATCH` otherwise.
+ *
+ * @internal
+ *
+ * The type `regex_t *` is defined in `<ruby/onigmo.h>`, _and_
+ * _conflicts_ with POSIX's `<regex.h>`. We can no longer save the situation
+ * at this point. Just don't mix the two.
+ */
+OnigPosition rb_reg_onig_match(VALUE re, VALUE str,
+ OnigPosition (*match)(regex_t *reg, VALUE str, struct re_registers *regs, void *args),
+ void *args, struct re_registers *regs);
+
+/**
+ * Duplicates a match data. This is roughly the same as `onig_region_copy()`,
+ * except it tries to GC when there is not enough memory.
+ *
+ * @param[out] dst Target registers to fill.
+ * @param[in] src Source registers to duplicate.
+ * @exception rb_eNoMemError Not enough memory.
+ * @retval 0 Successful
+ * @retval ONIGERR_MEMORY Not enough memory, even after GC (unlikely).
+ * @post `dst` has identical contents to `src`.
+ *
+ * @internal
+ *
+ * It seems this function is here for `ext/strscan` and nothing else.
+ */
+int rb_reg_region_copy(struct re_registers *dst, const struct re_registers *src);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_RE_H */
diff --git a/include/ruby/regex.h b/include/ruby/regex.h
new file mode 100644
index 0000000000..53278173f8
--- /dev/null
+++ b/include/ruby/regex.h
@@ -0,0 +1,43 @@
+#ifndef ONIGURUMA_REGEX_H /*-*-C++-*-vi:se ft=cpp:*/
+#define ONIGURUMA_REGEX_H 1
+/**
+ * @author $Author$
+ * @copyright Copyright (C) 1993-2007 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#if defined(__cplusplus)
+extern "C" {
+#if 0
+} /* satisfy cc-mode */
+#endif
+#endif
+
+#ifdef RUBY
+#include "ruby/oniguruma.h"
+#else
+#include "oniguruma.h"
+#endif
+
+RUBY_SYMBOL_EXPORT_BEGIN
+
+#ifndef ONIG_RUBY_M17N
+
+ONIG_EXTERN OnigEncoding OnigEncDefaultCharEncoding;
+
+#define mbclen(p,e,enc) rb_enc_mbclen((p),(e),(enc))
+
+#endif /* ifndef ONIG_RUBY_M17N */
+
+RUBY_SYMBOL_EXPORT_END
+
+#if defined(__cplusplus)
+#if 0
+{ /* satisfy cc-mode */
+#endif
+} /* extern "C" { */
+#endif
+
+#endif /* ONIGURUMA_REGEX_H */
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
new file mode 100644
index 0000000000..ca794bcaeb
--- /dev/null
+++ b/include/ruby/ruby.h
@@ -0,0 +1,446 @@
+#ifndef RUBY_RUBY_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_RUBY_H 1
+/**
+ * @file
+ * @author $Author$
+ * @date Thu Jun 10 14:26:32 JST 1993
+ * @copyright Copyright (C) 1993-2008 Yukihiro Matsumoto
+ * @copyright Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ * @copyright Copyright (C) 2000 Information-technology Promotion Agency, Japan
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#include "ruby/internal/config.h"
+
+/* @shyouhei doesn't understand why we need <intrinsics.h> at this very
+ * beginning of the entire <ruby.h> circus. */
+#ifdef HAVE_INTRINSICS_H
+# include <intrinsics.h>
+#endif
+
+#include <stdarg.h>
+
+#include "defines.h"
+#include "ruby/internal/abi.h"
+#include "ruby/internal/anyargs.h"
+#include "ruby/internal/arithmetic.h"
+#include "ruby/internal/core.h"
+#include "ruby/internal/ctype.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/internal/error.h"
+#include "ruby/internal/eval.h"
+#include "ruby/internal/event.h"
+#include "ruby/internal/fl_type.h"
+#include "ruby/internal/gc.h"
+#include "ruby/internal/glob.h"
+#include "ruby/internal/globals.h"
+#include "ruby/internal/has/warning.h"
+#include "ruby/internal/interpreter.h"
+#include "ruby/internal/iterator.h"
+#include "ruby/internal/memory.h"
+#include "ruby/internal/method.h"
+#include "ruby/internal/module.h"
+#include "ruby/internal/newobj.h"
+#include "ruby/internal/scan_args.h"
+#include "ruby/internal/special_consts.h"
+#include "ruby/internal/symbol.h"
+#include "ruby/internal/value.h"
+#include "ruby/internal/value_type.h"
+#include "ruby/internal/variable.h"
+#include "ruby/assert.h"
+#include "ruby/backward/2/assume.h"
+#include "ruby/backward/2/inttypes.h"
+#include "ruby/backward/2/limits.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/* Module#methods, #singleton_methods and so on return Symbols */
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define USE_SYMBOL_AS_METHOD_NAME 1
+
+/**
+ * Converts an object to a path. It first tries `#to_path` method if any, then
+ * falls back to `#to_str` method.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @exception rb_eArgError `obj` contains a NUL byte.
+ * @exception rb_eTypeError `obj` is not path-ish.
+ * @exception rb_eEncCompatError No encoding conversion from `obj` to path.
+ * @return Converted path object.
+ */
+VALUE rb_get_path(VALUE obj);
+
+/**
+ * Ensures that the parameter object is a path.
+ *
+ * @param[in,out] v Arbitrary ruby object.
+ * @exception rb_eArgError `v` contains a NUL byte.
+ * @exception rb_eTypeError `v` is not path-ish.
+ * @exception rb_eEncCompatError `v` is not path-compatible.
+ * @post `v` is a path.
+ */
+#define FilePathValue(v) (RB_GC_GUARD(v) = rb_get_path(v))
+
+/**
+ * @deprecated This function is an alias of rb_get_path() now. The part that
+ * did "no_checksafe" was deleted. It remains here because of no
+ * harm.
+ */
+VALUE rb_get_path_no_checksafe(VALUE);
+
+/**
+ * This macro actually does the same thing as #FilePathValue now. The "String"
+ * part indicates that this is for when a string is treated like a pathname,
+ * rather than the actual pathname on the file systems. For examples:
+ * `Dir.fnmatch?`, `File.join`, `File.basename`, etc.
+ */
+#define FilePathStringValue(v) ((v) = rb_get_path(v))
+
+/** @cond INTERNAL_MACRO */
+#if defined(HAVE_BUILTIN___BUILTIN_CONSTANT_P) && defined(HAVE_STMT_AND_DECL_IN_EXPR)
+# define rb_varargs_argc_check_runtime(argc, vargc) \
+ (((argc) <= (vargc)) ? (argc) : \
+ (rb_fatal("argc(%d) exceeds actual arguments(%d)", \
+ argc, vargc), 0))
+# define rb_varargs_argc_valid_p(argc, vargc) \
+ ((argc) == 0 ? (vargc) <= 1 : /* [ruby-core:85266] [Bug #14425] */ \
+ (argc) == (vargc))
+# if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
+# ifdef HAVE_ATTRIBUTE_ERRORFUNC
+ERRORFUNC((" argument length doesn't match"), int rb_varargs_bad_length(int,int));
+# else
+# define rb_varargs_bad_length(argc, vargc) \
+ ((argc)/rb_varargs_argc_valid_p(argc, vargc))
+# endif
+# define rb_varargs_argc_check(argc, vargc) \
+ __builtin_choose_expr(__builtin_constant_p(argc), \
+ (rb_varargs_argc_valid_p(argc, vargc) ? (argc) : \
+ rb_varargs_bad_length(argc, vargc)), \
+ rb_varargs_argc_check_runtime(argc, vargc))
+# else
+# define rb_varargs_argc_check(argc, vargc) \
+ rb_varargs_argc_check_runtime(argc, vargc)
+# endif
+#endif
+/** @endcond */
+
+/**
+ * Queries the name of the passed class.
+ *
+ * @param[in] klass An instance of a class.
+ * @return The name of `klass`.
+ * @note Return value is managed by our GC. Don't free.
+ */
+const char *rb_class2name(VALUE klass);
+
+/**
+ * Queries the name of the class of the passed object.
+ *
+ * @param[in] obj Arbitrary ruby object.
+ * @return The name of the class of `obj`.
+ * @note Return value is managed by our GC. Don't free.
+ */
+const char *rb_obj_classname(VALUE obj);
+
+/**
+ * Inspects an object. It first calls the argument's `#inspect` method, then
+ * feeds its result string into ::rb_stdout.
+ *
+ * This is identical to Ruby level `Kernel#p`, except it takes only one object.
+ *
+ * @internal
+ *
+ * Above description is in fact inaccurate. This API interfaces with Ractors.
+ */
+void rb_p(VALUE obj);
+
+/**
+ * This function is an optimised version of calling `#==`. It checks equality
+ * between two objects by first doing a fast identity check using using C's
+ * `==` (same as `BasicObject#equal?`). If that check fails, it calls `#==`
+ * dynamically. This optimisation actually affects semantics, because when
+ * `#==` returns false for the same object obj, `rb_equal(obj, obj)` would
+ * still return true. This happens for `Float::NAN`, where `Float::NAN ==
+ * Float::NAN` is `false`, but `rb_equal(Float::NAN, Float::NAN)` is `true`.
+ *
+ * @param[in] lhs Comparison LHS.
+ * @param[in] rhs Comparison RHS.
+ * @retval RUBY_Qtrue They are the same.
+ * @retval RUBY_Qfalse They are different.
+ */
+VALUE rb_equal(VALUE lhs, VALUE rhs);
+
+/**
+ * Identical to rb_require_string(), except it takes C's string instead of
+ * Ruby's.
+ *
+ * @param[in] feature Name of a feature, e.g. `"json"`.
+ * @exception rb_eLoadError No such feature.
+ * @exception rb_eRuntimeError `$"` is frozen; unable to push.
+ * @retval RUBY_Qtrue The feature is loaded for the first time.
+ * @retval RUBY_Qfalse The feature has already been loaded.
+ * @post `$"` is updated.
+ */
+VALUE rb_require(const char *feature);
+
+#include "ruby/intern.h"
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define RUBY_VM 1 /* YARV */
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define HAVE_NATIVETHREAD
+
+/**
+ * Queries if the thread which calls this function is a ruby's thread.
+ * "Ruby's" in this context is a thread created using one of our APIs like
+ * rb_thread_create(). There are distinctions between ruby's and other
+ * threads. For instance calling ruby methods are allowed only from inside of
+ * a ruby's thread.
+ *
+ * @retval 1 The current thread is a Ruby's thread.
+ * @retval 0 The current thread is a random thread from outside of Ruby.
+ */
+int ruby_native_thread_p(void);
+
+/**
+ * @private
+ *
+ * This macro is for internal use. Must be a mistake to place here.
+ */
+#define InitVM(ext) {void InitVM_##ext(void);InitVM_##ext();}
+
+RBIMPL_ATTR_NONNULL((3))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 4)
+/**
+ * Our own locale-insensitive version of `snprintf(3)`. It can also be seen as
+ * a routine identical to rb_sprintf(), except it writes back to the passed
+ * buffer instead of allocating a new Ruby object.
+ *
+ * @param[out] str Return buffer
+ * @param[in] n Number of bytes of `str`.
+ * @param[in] fmt A `printf`-like format specifier.
+ * @param[in] ... Variadic number of contents to format.
+ * @return Number of bytes that would have been written to `str`, if `n`
+ * was large enough. Comparing this to `n` can give you insights
+ * that the buffer is too small or too big. Especially passing 0
+ * to `n` gives you the exact number of bytes necessary to hold
+ * the result string without writing anything to anywhere.
+ * @post `str` holds up to `n-1` bytes of formatted contents (and the
+ * terminating NUL character.)
+ */
+int ruby_snprintf(char *str, size_t n, char const *fmt, ...);
+
+RBIMPL_ATTR_NONNULL((3))
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 3, 0)
+/**
+ * Identical to ruby_snprintf(), except it takes a `va_list`. It can also be
+ * seen as a routine identical to rb_vsprintf(), except it writes back to the
+ * passed buffer instead of allocating a new Ruby object.
+ *
+ * @param[out] str Return buffer
+ * @param[in] n Number of bytes of `str`.
+ * @param[in] fmt A `printf`-like format specifier.
+ * @param[in] ap Contents to format.
+ * @return Number of bytes that would have been written to `str`, if `n`
+ * was large enough. Comparing this to `n` can give you insights
+ * that the buffer is too small or too big. Especially passing 0
+ * to `n` gives you the exact number of bytes necessary to hold
+ * the result string without writing anything to anywhere.
+ * @post `str` holds up to `n-1` bytes of formatted contents (and the
+ * terminating NUL character.)
+ */
+int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
+
+#include <errno.h>
+
+/**
+ * @name Errno handling routines for userland threads
+ * @note POSIX chapter 2 section 3 states that for each thread of a process,
+ * the value of `errno` shall not be affected by function calls or
+ * assignments to `errno` by other threads.
+ *
+ * Soooo this `#define errno` below seems like a noob mistake at first sight.
+ * If you look at its actual implementation, the functions are just adding one
+ * level of indirection. It doesn't make any sense sorry? But yes! @ko1 told
+ * @shyouhei that this is inevitable.
+ *
+ * The ultimate reason is because Ruby now has N:M threads implemented.
+ * Threads of that sort change their context in user land. A function can be
+ * "transferred" between threads in middle of their executions. Let us for
+ * instance consider:
+ *
+ * ```cxx
+ * void foo()
+ * {
+ * auto i = errno;
+ * close(0);
+ * errno = i;
+ * }
+ * ```
+ *
+ * This function (if ran under our Ractor) could change its running thread at
+ * the `close` function. But the two `errno` invocations are different! Look
+ * how the source code above is compiled by clang 17 with `-O3` flag @ Linux:
+ *
+ * ```
+ * foo(int): # @foo(int)
+ * push rbp
+ * push r14
+ * push rbx
+ * mov ebx, edi
+ * call __errno_location@PLT
+ * mov r14, rax
+ * mov ebp, dword ptr [rax]
+ * mov edi, ebx
+ * call close@PLT
+ * mov dword ptr [r14], ebp
+ * pop rbx
+ * pop r14
+ * pop rbp
+ * ret
+ * ```
+ *
+ * Notice how `__errno_location@PLT` is `call`-ed only once. The compiler
+ * assumes that the location of `errno` does not change during a function call.
+ * Sadly this is no longer true for us. The `close@PLT` now changes threads,
+ * which should also change where `errno` is stored.
+ *
+ * With the `#define errno` below the compilation result changes to this:
+ *
+ * ```
+ * foo(int): # @foo(int)
+ * push rbp
+ * push rbx
+ * push rax
+ * mov ebx, edi
+ * call rb_errno_ptr()@PLT
+ * mov ebp, dword ptr [rax]
+ * mov edi, ebx
+ * call close@PLT
+ * call rb_errno_ptr()@PLT
+ * mov dword ptr [rax], ebp
+ * add rsp, 8
+ * pop rbx
+ * pop rbp
+ * ret
+ * ```
+ *
+ * Which fixes the problem.
+ */
+
+/**
+ * Identical to system `errno`.
+ *
+ * @return The last set `errno` number.
+ */
+int rb_errno(void);
+
+/**
+ * Set the errno.
+ *
+ * @param err New `errno`.
+ * @post `errno` is now set to `err`.
+ */
+void rb_errno_set(int err);
+
+/**
+ * The location of `errno`
+ *
+ * @return The (thread-specific) location of `errno`.
+ */
+int *rb_errno_ptr(void);
+
+/**
+ * Not sure if it is necessary for extension libraries but this is where the
+ * "bare" errno is located.
+ *
+ * @return The location of `errno`.
+ */
+static inline int *
+rb_orig_errno_ptr(void)
+{
+ return &errno;
+}
+
+#define rb_orig_errno errno /**< System-provided original `errno`. */
+#undef errno
+#define errno (*rb_errno_ptr()) /**< Ractor-aware version of `errno`. */
+
+/** @} */
+
+
+/** @cond INTERNAL_MACRO */
+#if RBIMPL_HAS_WARNING("-Wgnu-zero-variadic-macro-arguments")
+# /* Skip it; clang -pedantic doesn't like the following */
+#elif defined(__GNUC__) && defined(HAVE_VA_ARGS_MACRO) && defined(__OPTIMIZE__)
+# define rb_yield_values(argc, ...) \
+__extension__({ \
+ const int rb_yield_values_argc = (argc); \
+ const VALUE rb_yield_values_args[] = {__VA_ARGS__}; \
+ const int rb_yield_values_nargs = \
+ (int)(sizeof(rb_yield_values_args) / sizeof(VALUE)); \
+ rb_yield_values2( \
+ rb_varargs_argc_check(rb_yield_values_argc, rb_yield_values_nargs), \
+ rb_yield_values_nargs ? rb_yield_values_args : NULL); \
+ })
+
+# define rb_funcall(recv, mid, argc, ...) \
+__extension__({ \
+ const int rb_funcall_argc = (argc); \
+ const VALUE rb_funcall_args[] = {__VA_ARGS__}; \
+ const int rb_funcall_nargs = \
+ (int)(sizeof(rb_funcall_args) / sizeof(VALUE)); \
+ rb_funcallv(recv, mid, \
+ rb_varargs_argc_check(rb_funcall_argc, rb_funcall_nargs), \
+ rb_funcall_nargs ? rb_funcall_args : NULL); \
+ })
+#endif
+/** @endcond */
+
+#ifndef RUBY_DONT_SUBST
+#include "ruby/subst.h"
+#endif
+
+#if !defined RUBY_EXPORT && !defined RUBY_NO_OLD_COMPATIBILITY
+# include "ruby/backward.h"
+#endif
+
+#ifndef RUBY__ASAN_DEFAULT_OPTIONS
+# define RUBY__ASAN_DEFAULT_OPTIONS
+#endif
+
+#define RUBY_GLOBAL_SETUP \
+ RUBY__ASAN_DEFAULT_OPTIONS \
+ /* RUBY_GLOBAL_SETUP end */
+
+#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
+int rb_wasm_rt_start(int (*)(int, char **), int, char **);
+# define ruby_start_main rb_wasm_rt_start
+#else
+# define ruby_start_main(main, argc, argv) main(argc, argv)
+#endif
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_RUBY_H */
diff --git a/include/ruby/st.h b/include/ruby/st.h
new file mode 100644
index 0000000000..f35ab43603
--- /dev/null
+++ b/include/ruby/st.h
@@ -0,0 +1,199 @@
+/* This is a public domain general purpose hash table package
+ originally written by Peter Moore @ UCB.
+
+ The hash table data structures were redesigned and the package was
+ rewritten by Vladimir Makarov <vmakarov@redhat.com>. */
+
+#ifndef RUBY_ST_H
+#define RUBY_ST_H 1
+
+#if defined(__cplusplus)
+extern "C" {
+#if 0
+} /* satisfy cc-mode */
+#endif
+#endif
+
+#include "ruby/defines.h"
+
+RUBY_SYMBOL_EXPORT_BEGIN
+
+#if SIZEOF_LONG == SIZEOF_VOIDP
+typedef unsigned long st_data_t;
+#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
+typedef unsigned LONG_LONG st_data_t;
+#else
+# error ---->> st.c requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
+#endif
+#define ST_DATA_T_DEFINED
+
+#ifndef CHAR_BIT
+# ifdef HAVE_LIMITS_H
+# include <limits.h>
+# else
+# define CHAR_BIT 8
+# endif
+#endif
+#ifndef _
+# define _(args) args
+#endif
+#ifndef ANYARGS
+# ifdef __cplusplus
+# define ANYARGS ...
+# else
+# define ANYARGS
+# endif
+#endif
+
+typedef struct st_table st_table;
+
+typedef st_data_t st_index_t;
+
+/* Maximal value of unsigned integer type st_index_t. */
+#define MAX_ST_INDEX_VAL (~(st_index_t) 0)
+
+typedef int st_compare_func(st_data_t, st_data_t);
+typedef st_index_t st_hash_func(st_data_t);
+
+typedef char st_check_for_sizeof_st_index_t[SIZEOF_VOIDP == (int)sizeof(st_index_t) ? 1 : -1];
+#define SIZEOF_ST_INDEX_T SIZEOF_VOIDP
+
+struct st_hash_type {
+ int (*compare)(st_data_t, st_data_t); /* st_compare_func* */
+ st_index_t (*hash)(st_data_t); /* st_hash_func* */
+};
+
+#define ST_INDEX_BITS (SIZEOF_ST_INDEX_T * CHAR_BIT)
+
+#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR) && defined(HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P)
+# define ST_DATA_COMPATIBLE_P(type) \
+ __builtin_choose_expr(__builtin_types_compatible_p(type, st_data_t), 1, 0)
+#else
+# define ST_DATA_COMPATIBLE_P(type) 0
+#endif
+
+typedef struct st_table_entry st_table_entry;
+
+struct st_table_entry; /* defined in st.c */
+
+struct st_table {
+ /* Cached features of the table -- see st.c for more details. */
+ unsigned char entry_power, bin_power, size_ind;
+ /* How many times the table was rebuilt. */
+ unsigned int rebuilds_num;
+ const struct st_hash_type *type;
+ /* Number of entries currently in the table. */
+ st_index_t num_entries;
+ /* Array of bins used for access by keys. */
+ st_index_t *bins;
+ /* Start and bound index of entries in array entries.
+ entries_starts and entries_bound are in interval
+ [0,allocated_entries]. */
+ st_index_t entries_start, entries_bound;
+ /* Array of size 2^entry_power. */
+ st_table_entry *entries;
+};
+
+#define st_is_member(table,key) st_lookup((table),(key),(st_data_t *)0)
+
+enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE};
+
+size_t rb_st_table_size(const struct st_table *tbl);
+#define st_table_size rb_st_table_size
+st_table *rb_st_init_table(const struct st_hash_type *);
+#define st_init_table rb_st_init_table
+st_table *rb_st_init_table_with_size(const struct st_hash_type *, st_index_t);
+#define st_init_table_with_size rb_st_init_table_with_size
+st_table *rb_st_init_numtable(void);
+#define st_init_numtable rb_st_init_numtable
+st_table *rb_st_init_numtable_with_size(st_index_t);
+#define st_init_numtable_with_size rb_st_init_numtable_with_size
+st_table *rb_st_init_strtable(void);
+#define st_init_strtable rb_st_init_strtable
+st_table *rb_st_init_strtable_with_size(st_index_t);
+#define st_init_strtable_with_size rb_st_init_strtable_with_size
+st_table *rb_st_init_strcasetable(void);
+#define st_init_strcasetable rb_st_init_strcasetable
+st_table *rb_st_init_strcasetable_with_size(st_index_t);
+#define st_init_strcasetable_with_size rb_st_init_strcasetable_with_size
+int rb_st_delete(st_table *, st_data_t *, st_data_t *); /* returns 0:notfound 1:deleted */
+#define st_delete rb_st_delete
+int rb_st_delete_safe(st_table *, st_data_t *, st_data_t *, st_data_t);
+#define st_delete_safe rb_st_delete_safe
+int rb_st_shift(st_table *, st_data_t *, st_data_t *); /* returns 0:notfound 1:deleted */
+#define st_shift rb_st_shift
+int rb_st_insert(st_table *, st_data_t, st_data_t);
+#define st_insert rb_st_insert
+int rb_st_insert2(st_table *, st_data_t, st_data_t, st_data_t (*)(st_data_t));
+#define st_insert2 rb_st_insert2
+int rb_st_lookup(st_table *, st_data_t, st_data_t *);
+#define st_lookup rb_st_lookup
+int rb_st_get_key(st_table *, st_data_t, st_data_t *);
+#define st_get_key rb_st_get_key
+typedef int st_update_callback_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing);
+/* *key may be altered, but must equal to the old key, i.e., the
+ * results of hash() are same and compare() returns 0, otherwise the
+ * behavior is undefined */
+int rb_st_update(st_table *table, st_data_t key, st_update_callback_func *func, st_data_t arg);
+#define st_update rb_st_update
+typedef int st_foreach_callback_func(st_data_t, st_data_t, st_data_t);
+typedef int st_foreach_check_callback_func(st_data_t, st_data_t, st_data_t, int);
+int rb_st_foreach_with_replace(st_table *tab, st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg);
+#define st_foreach_with_replace rb_st_foreach_with_replace
+int rb_st_foreach(st_table *, st_foreach_callback_func *, st_data_t);
+#define st_foreach rb_st_foreach
+int rb_st_foreach_check(st_table *, st_foreach_check_callback_func *, st_data_t, st_data_t);
+#define st_foreach_check rb_st_foreach_check
+st_index_t rb_st_keys(st_table *table, st_data_t *keys, st_index_t size);
+#define st_keys rb_st_keys
+st_index_t rb_st_keys_check(st_table *table, st_data_t *keys, st_index_t size, st_data_t never);
+#define st_keys_check rb_st_keys_check
+st_index_t rb_st_values(st_table *table, st_data_t *values, st_index_t size);
+#define st_values rb_st_values
+st_index_t rb_st_values_check(st_table *table, st_data_t *values, st_index_t size, st_data_t never);
+#define st_values_check rb_st_values_check
+void rb_st_add_direct(st_table *, st_data_t, st_data_t);
+#define st_add_direct rb_st_add_direct
+void rb_st_free_table(st_table *);
+#define st_free_table rb_st_free_table
+void rb_st_cleanup_safe(st_table *, st_data_t);
+#define st_cleanup_safe rb_st_cleanup_safe
+void rb_st_clear(st_table *);
+#define st_clear rb_st_clear
+st_table *rb_st_copy(st_table *);
+#define st_copy rb_st_copy
+CONSTFUNC(int rb_st_numcmp(st_data_t, st_data_t));
+#define st_numcmp rb_st_numcmp
+CONSTFUNC(st_index_t rb_st_numhash(st_data_t));
+#define st_numhash rb_st_numhash
+PUREFUNC(int rb_st_locale_insensitive_strcasecmp(const char *s1, const char *s2));
+#define st_locale_insensitive_strcasecmp rb_st_locale_insensitive_strcasecmp
+PUREFUNC(int rb_st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n));
+#define st_locale_insensitive_strncasecmp rb_st_locale_insensitive_strncasecmp
+#define st_strcasecmp rb_st_locale_insensitive_strcasecmp
+#define st_strncasecmp rb_st_locale_insensitive_strncasecmp
+PUREFUNC(size_t rb_st_memsize(const st_table *));
+#define st_memsize rb_st_memsize
+PUREFUNC(st_index_t rb_st_hash(const void *ptr, size_t len, st_index_t h));
+#define st_hash rb_st_hash
+CONSTFUNC(st_index_t rb_st_hash_uint32(st_index_t h, uint32_t i));
+#define st_hash_uint32 rb_st_hash_uint32
+CONSTFUNC(st_index_t rb_st_hash_uint(st_index_t h, st_index_t i));
+#define st_hash_uint rb_st_hash_uint
+CONSTFUNC(st_index_t rb_st_hash_end(st_index_t h));
+#define st_hash_end rb_st_hash_end
+CONSTFUNC(st_index_t rb_st_hash_start(st_index_t h));
+#define st_hash_start(h) ((st_index_t)(h))
+
+void rb_hash_bulk_insert_into_st_table(long, const VALUE *, VALUE);
+
+RUBY_SYMBOL_EXPORT_END
+
+#if defined(__cplusplus)
+#if 0
+{ /* satisfy cc-mode */
+#endif
+} /* extern "C" { */
+#endif
+
+#endif /* RUBY_ST_H */
diff --git a/include/ruby/subst.h b/include/ruby/subst.h
new file mode 100644
index 0000000000..d7b9a63050
--- /dev/null
+++ b/include/ruby/subst.h
@@ -0,0 +1,26 @@
+#ifndef RUBY_SUBST_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_SUBST_H 1
+/**
+ * @author Ruby developers <ruby-core@ruby-lang.org>
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+
+#undef snprintf
+#undef vsnprintf
+#define snprintf ruby_snprintf
+#define vsnprintf ruby_vsnprintf
+
+#ifdef BROKEN_CLOSE
+#undef getpeername
+#define getpeername ruby_getpeername
+#undef getsockname
+#define getsockname ruby_getsockname
+#undef shutdown
+#define shutdown ruby_shutdown
+#undef close
+#define close ruby_close
+#endif
+#endif
diff --git a/include/ruby/thread.h b/include/ruby/thread.h
new file mode 100644
index 0000000000..2fa01229e2
--- /dev/null
+++ b/include/ruby/thread.h
@@ -0,0 +1,345 @@
+#ifndef RUBY_THREAD_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_THREAD_H 1
+/**
+ * @file
+ * @author $Author: matz $
+ * @date Tue Jul 10 17:35:43 JST 2012
+ * @copyright Copyright (C) 2007 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ */
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/intern/thread.h" /* rb_unblock_function_t */
+#include "ruby/internal/dllexport.h"
+
+/**
+ * @name Flags for rb_nogvl()
+ *
+ * @{
+ */
+
+/**
+ * Passing this flag to rb_nogvl() prevents it from checking interrupts.
+ * Interrupts can impact your program negatively. For instance consider
+ * following callback function:
+ *
+ * ```CXX
+ * static inline int fd; // set elsewhere.
+ * static inline auto callback(auto buf) {
+ * auto tmp = ruby_xmalloc(BUFSIZ);
+ * auto ret = ruby_xmalloc(sizeof(ssize_t)); // (a)
+ * auto n = read(fd, tmp, BUFSIZ); // (b)
+ * memcpy(buf, tmp, n); // (c)
+ * memcpy(ret, n, sizeof(n));
+ * ruby_xfree(tmp);
+ * return ret;
+ * }
+ * ```
+ *
+ * Here, if it gets interrupted at (a) or (b), `read(2)` is cancelled and this
+ * function leaks memory (which is not a good thing of course, but...). But if
+ * it gets interrupted at (c), where `read(2)` is already done, interruption is
+ * way more catastrophic because what was read gets lost. To reroute this kind
+ * of problem you should set this flag. And check interrupts elsewhere at your
+ * own risk.
+ */
+#define RB_NOGVL_INTR_FAIL (0x1)
+
+/**
+ * Passing this flag to rb_nogvl() indicates that the passed UBF is
+ * async-signal-safe. An UBF could be async safe, and that makes things
+ * simpler. However async unsafe UBFs are just okay. If unsure, you can
+ * safely leave it unspecified.
+ *
+ * @internal
+ *
+ * This makes sense only in case of POSIX threads.
+ */
+#define RB_NOGVL_UBF_ASYNC_SAFE (0x2)
+
+/**
+ * Passing this flag to rb_nogvl() indicates that the passed function
+ * is safe to offload to a background thread or work pool. In other words, the
+ * function is safe to run using a fiber scheduler's `blocking_operation_wait`.
+ * hook.
+ *
+ * If your function depends on thread-local storage, or thread-specific data
+ * operations/data structures, you should not set this flag, as
+ * these operations may behave differently (or fail) when run in a different
+ * thread/context (e.g. unlocking a mutex).
+ */
+#define RB_NOGVL_OFFLOAD_SAFE (0x4)
+
+/** @} */
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * (Re-)acquires the GVL. This manoeuvre makes it possible for an out-of-GVL
+ * routine to one-shot call a ruby method.
+ *
+ * What this function does:
+ *
+ * 1. Blocks until it acquires the GVL.
+ * 2. Calls the passed function.
+ * 3. Releases the GVL.
+ * 4. Returns what was returned form the passed function.
+ *
+ * @param[in] func What to call with GVL.
+ * @param[in,out] data1 Passed as-is to `func`.
+ * @return What was returned from `func`.
+ * @warning `func` must not return a Ruby object. If it did such return
+ * value would escape from GC's scope; would not be marked.
+ * @warning Global escapes from this function just yield whatever fatal
+ * undefined behaviours. You must make sure that `func` does
+ * not raise, by properly rescuing everything using
+ * e.g. rb_protect().
+ * @warning You cannot convert a non-Ruby thread into a Ruby thread
+ * using this API. This function makes sense only from inside
+ * of a rb_thread_call_without_gvl()'s callback.
+ */
+void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Allows the passed function to run in parallel with other Ruby threads.
+ *
+ * What this function does:
+ *
+ * 1. Checks (and handles) pending interrupts.
+ * 2. Releases the GVL. (Others can run here in parallel...)
+ * 3. Calls the passed function.
+ * 4. Blocks until it re-acquires the GVL.
+ * 5. Checks interrupts that happened between 2 to 4.
+ *
+ * In case other threads interfaced with this thread using rb_thread_kill()
+ * etc., the passed UBF is additionally called. See ::rb_unblock_function_t
+ * for details.
+ *
+ * Unlike rb_thread_call_without_gvl2() this function also reacts to signals
+ * etc.
+ *
+ * @param[in] func A function to call without GVL.
+ * @param[in,out] data1 Passed as-is to `func`.
+ * @param[in] ubf An UBF to cancel `func`.
+ * @param[in,out] data2 Passed as-is to `ubf`.
+ * @return What `func` returned, or 0 in case `ubf` cancelled `func`.
+ * @warning You cannot use most of Ruby C APIs like calling methods or
+ * raising exceptions from any of the functions passed to it.
+ * If that is dead necessary use rb_thread_call_with_gvl() to
+ * re-acquire the GVL.
+ * @warning In short, this API is difficult. @ko1 recommends you to use
+ * other ways if any. We lack experiences to use this API. If
+ * you find any corner cases etc., please report it to the
+ * devs.
+ * @warning Releasing and re-acquiring the GVL are expensive operations.
+ * For a short-running `func`, it might be faster to just call
+ * `func` with blocking everything else. Be sure to benchmark
+ * your code to see if it is actually worth releasing the GVL.
+ */
+void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
+ rb_unblock_function_t *ubf, void *data2);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_thread_call_without_gvl(), except it does not interface with
+ * signals etc. As described in #RB_NOGVL_INTR_FAIL, interrupts can hurt you.
+ * In case this function detects an interrupt, it returns immediately. You can
+ * record progress of your callback and check it after returning from this
+ * function.
+ *
+ * What this function does:
+ *
+ * 1. Checks for pending interrupts and if any, just returns.
+ * 2. Releases the GVL. (Others can run here in parallel...)
+ * 3. Calls the passed function.
+ * 4. Blocks until it re-acquires the GVL.
+ *
+ * @param[in] func A function to call without GVL.
+ * @param[in,out] data1 Passed as-is to `func`.
+ * @param[in] ubf An UBF to cancel `func`.
+ * @param[in,out] data2 Passed as-is to `ubf`.
+ * @return What `func` returned, or 0 in case `func` did not return.
+ */
+void *rb_thread_call_without_gvl2(void *(*func)(void *), void *data1,
+ rb_unblock_function_t *ubf, void *data2);
+
+/*
+ * XXX: unstable/unapproved - out-of-tree code should NOT not depend
+ * on this until it hits Ruby 2.6.1
+ */
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Identical to rb_thread_call_without_gvl(), except it additionally takes
+ * "flags" that change the behaviour.
+ *
+ * @param[in] func A function to call without GVL.
+ * @param[in,out] data1 Passed as-is to `func`.
+ * @param[in] ubf An UBF to cancel `func`.
+ * @param[in,out] data2 Passed as-is to `ubf`.
+ * @param[in] flags Flags.
+ * @return What `func` returned, or 0 in case `func` did not return.
+ */
+void *rb_nogvl(void *(*func)(void *), void *data1,
+ rb_unblock_function_t *ubf, void *data2,
+ int flags);
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
+#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_AFTER 0x01
+
+/**
+ * @private
+ * @deprecated It seems even in the old days it made no sense...?
+ */
+#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_
+
+/**
+ * Declare the current Ruby thread should acquire a dedicated
+ * native thread on M:N thread scheduler.
+ *
+ * If a C extension (or a library which the extension relies on) should
+ * keep to run on a native thread (e.g. using thread-local-storage),
+ * this function allocates a dedicated native thread for the thread.
+ *
+ * @return `false` if the thread already running on a dedicated native
+ * thread. Otherwise `true`.
+ */
+bool rb_thread_lock_native_thread(void);
+
+/**
+ * Triggered when a new thread is started.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_STARTED 1 << 0
+
+/**
+* Triggered when a thread attempt to acquire the GVL.
+*
+* @note The callback will be called *without* the GVL held.
+*/
+#define RUBY_INTERNAL_THREAD_EVENT_READY 1 << 1 /** acquiring GVL */
+
+/**
+ * Triggered when a thread successfully acquired the GVL.
+ *
+ * @note The callback will be called *with* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_RESUMED 1 << 2 /** acquired GVL */
+
+/**
+ * Triggered when a thread released the GVL.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_SUSPENDED 1 << 3 /** released GVL */
+
+/**
+ * Triggered when a thread exits.
+ *
+ * @note The callback will be called *without* the GVL held.
+ */
+#define RUBY_INTERNAL_THREAD_EVENT_EXITED 1 << 4 /** thread terminated */
+
+#define RUBY_INTERNAL_THREAD_EVENT_MASK 0xff /** All Thread events */
+
+typedef struct rb_internal_thread_event_data {
+ VALUE thread;
+} rb_internal_thread_event_data_t;
+
+typedef void (*rb_internal_thread_event_callback)(rb_event_flag_t event,
+ const rb_internal_thread_event_data_t *event_data,
+ void *user_data);
+typedef struct rb_internal_thread_event_hook rb_internal_thread_event_hook_t;
+
+/**
+ * Registers a thread event hook function.
+ *
+ * @param[in] func A callback.
+ * @param[in] events A set of events that `func` should run.
+ * @param[in] data Passed as-is to `func`.
+ * @return An opaque pointer to the hook, to unregister it later.
+ * @note This functionality is a noop on Windows and WebAssembly.
+ * @note The callback will be called without the GVL held, except for the
+ * RESUMED event.
+ * @note Callbacks are not guaranteed to be executed on the native threads
+ * that corresponds to the Ruby thread. To identify which Ruby thread
+ * the event refers to, you must use `event_data->thread`.
+ * @warning This function MUST not be called from a thread event callback.
+ */
+rb_internal_thread_event_hook_t *rb_internal_thread_add_event_hook(
+ rb_internal_thread_event_callback func, rb_event_flag_t events,
+ void *data);
+
+
+/**
+ * Unregister the passed hook.
+ *
+ * @param[in] hook. The hook to unregister.
+ * @return Whether the hook was found and unregistered.
+ * @note This functionality is a noop on Windows and WebAssembly.
+ * @warning This function MUST not be called from a thread event callback.
+*/
+bool rb_internal_thread_remove_event_hook(
+ rb_internal_thread_event_hook_t * hook);
+
+
+typedef int rb_internal_thread_specific_key_t;
+#define RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX 8
+/**
+ * Create a key to store thread specific data.
+ *
+ * These APIs are designed for tools using
+ * rb_internal_thread_event_hook APIs.
+ *
+ * Note that only `RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX` keys
+ * can be created. raises `ThreadError` if exceeded.
+ *
+ * Usage:
+ * // at initialize time:
+ * int tool_key; // gvar
+ * Init_tool() {
+ * tool_key = rb_internal_thread_specific_key_create();
+ * }
+ *
+ * // at any timing:
+ * rb_internal_thread_specific_set(thread, tool_key, per_thread_data);
+ * ...
+ * per_thread_data = rb_internal_thread_specific_get(thread, tool_key);
+ */
+rb_internal_thread_specific_key_t rb_internal_thread_specific_key_create(void);
+
+/**
+ * Get thread and tool specific data.
+ *
+ * This function is async signal safe and thread safe.
+ */
+void *rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key);
+
+/**
+ * Set thread and tool specific data.
+ *
+ * This function is async signal safe and thread safe.
+ */
+void rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data);
+
+/**
+ * Whether the current thread is holding the GVL.
+ *
+ * @return true if the current thread is holding the GVL, false otherwise.
+ */
+int ruby_thread_has_gvl_p(void);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_THREAD_H */
diff --git a/include/ruby/thread_native.h b/include/ruby/thread_native.h
new file mode 100644
index 0000000000..8217a67514
--- /dev/null
+++ b/include/ruby/thread_native.h
@@ -0,0 +1,210 @@
+#ifndef RUBY_THREAD_NATIVE_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_THREAD_NATIVE_H 1
+/**
+ * @file
+ * @author $Author: ko1 $
+ * @date Wed May 14 19:37:31 2014
+ * @copyright Copyright (C) 2014 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ *
+ * This file contains wrapper APIs for native thread primitives
+ * which Ruby interpreter uses.
+ *
+ * Now, we only support pthread and Windows threads.
+ *
+ * If you want to use Ruby's Mutex and so on to synchronize Ruby Threads,
+ * please use Mutex directly.
+ */
+
+#if defined(_WIN32)
+#include <windows.h>
+typedef HANDLE rb_nativethread_id_t;
+
+typedef union rb_thread_lock_union {
+ HANDLE mutex;
+ CRITICAL_SECTION crit;
+} rb_nativethread_lock_t;
+
+struct rb_thread_cond_struct {
+ struct cond_event_entry *next;
+ struct cond_event_entry *prev;
+};
+
+typedef struct rb_thread_cond_struct rb_nativethread_cond_t;
+
+#elif defined(HAVE_PTHREAD_H)
+
+#include <pthread.h>
+typedef pthread_t rb_nativethread_id_t;
+typedef pthread_mutex_t rb_nativethread_lock_t;
+typedef pthread_cond_t rb_nativethread_cond_t;
+
+#elif defined(__wasi__) // no-thread platforms
+
+typedef struct rb_nativethread_id_t *rb_nativethread_id_t;
+typedef struct rb_nativethread_lock_t *rb_nativethread_lock_t;
+typedef struct rb_nativethread_cond_t *rb_nativethread_cond_t;
+
+#elif defined(__DOXYGEN__)
+
+/** Opaque type that holds an ID of a native thread. */
+struct rb_nativethread_id_t;
+
+/** Opaque type that holds a lock. */
+struct rb_nativethread_lock_t;
+
+/** Opaque type that holds a condition variable. */
+struct rb_nativethread_cond_t;
+
+#else
+#error "unsupported thread type"
+
+#endif
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * Queries the ID of the native thread that is calling this function.
+ *
+ * @return The caller thread's native ID.
+ */
+rb_nativethread_id_t rb_nativethread_self(void);
+
+/**
+ * Fills the passed lock with an initial value.
+ *
+ * @param[out] lock A mutex to initialise.
+ * @post `lock` is updated to its initial state.
+ *
+ * @internal
+ *
+ * There is no data structure that analogous to pthread_once_t in ruby. It is
+ * pretty much tricky (if not impossible) to properly initialise a mutex
+ * exactly once.
+ */
+void rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock);
+
+/**
+ * Destroys the passed mutex.
+ *
+ * @param[out] lock A mutex to kill.
+ * @post `lock` is no longer eligible for other functions.
+ *
+ * @internal
+ *
+ * It is an undefined behaviour (see `pthread_mutex_destroy(3posix)`) to
+ * destroy a locked mutex. So it has to be unlocked. But an unlocked mutex
+ * can of course be locked by another thread. That's the ultimate reason why
+ * we do mutex. There is an inevitable race condition here. 2017 edition of
+ * IEEE 1003.1 issue 7 says in its rationale that "care must be taken". Care?
+ * How?
+ *
+ * @shyouhei thinks that POSIX is broken by design.
+ */
+void rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock);
+
+/**
+ * Blocks until the current thread obtains a lock.
+ *
+ * @param[out] lock A mutex to lock.
+ * @post `lock` is owned by the current native thread.
+ */
+void rb_nativethread_lock_lock(rb_nativethread_lock_t *lock);
+
+/**
+ * Releases a lock.
+ *
+ * @param[out] lock A mutex to unlock.
+ * @pre `lock` is owned by the current native thread.
+ * @post `lock` is not owned by the current native thread.
+ */
+void rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock);
+
+/** @alias{rb_nativethread_lock_lock} */
+void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
+
+/**
+ * Identical to rb_native_mutex_lock(), except it doesn't block in case
+ * rb_native_mutex_lock() would.
+ *
+ * @param[out] lock A mutex to lock.
+ * @retval 0 `lock` is successfully owned by the current thread.
+ * @retval EBUSY `lock` is owned by someone else.
+ */
+int rb_native_mutex_trylock(rb_nativethread_lock_t *lock);
+
+/** @alias{rb_nativethread_lock_unlock} */
+void rb_native_mutex_unlock(rb_nativethread_lock_t *lock);
+
+/** @alias{rb_nativethread_lock_initialize} */
+void rb_native_mutex_initialize(rb_nativethread_lock_t *lock);
+
+/** @alias{rb_nativethread_lock_destroy} */
+void rb_native_mutex_destroy(rb_nativethread_lock_t *lock);
+
+/**
+ * Signals a condition variable.
+ *
+ * @param[out] cond A condition variable to ping.
+ * @post More than one threads waiting for `cond` gets signalled.
+ * @note This function can spuriously wake multiple threads up.
+ * `pthread_cond_signal(3posix)` says it can even be "impossible
+ * to avoid the unblocking of more than one thread blocked on a
+ * condition variable". Just brace spurious wakeups.
+ */
+void rb_native_cond_signal(rb_nativethread_cond_t *cond);
+
+/**
+ * Signals a condition variable.
+ *
+ * @param[out] cond A condition variable to ping.
+ * @post All threads waiting for `cond` gets signalled.
+ */
+void rb_native_cond_broadcast(rb_nativethread_cond_t *cond);
+
+/**
+ * Waits for the passed condition variable to be signalled.
+ *
+ * @param[out] cond A condition variable to wait.
+ * @param[out] mutex A mutex.
+ * @pre `mutex` is owned by the current thread.
+ * @post `mutex` is owned by the current thread.
+ * @note This can wake up spuriously.
+ */
+void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex);
+
+/**
+ * Identical to rb_native_cond_wait(), except it additionally takes timeout in
+ * msec resolution. Timeouts can be detected by catching exceptions.
+ *
+ * @param[out] cond A condition variable to wait.
+ * @param[out] mutex A mutex.
+ * @param[in] msec Timeout.
+ * @exception rb_eSystemCallError `Errno::ETIMEDOUT` for timeout.
+ * @pre `mutex` is owned by the current thread.
+ * @post `mutex` is owned by the current thread.
+ * @note This can wake up spuriously.
+ */
+void rb_native_cond_timedwait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex, unsigned long msec);
+
+/**
+ * Fills the passed condition variable with an initial value.
+ *
+ * @param[out] cond A condition variable to initialise.
+ * @post `cond` is updated to its initial state.
+ */
+void rb_native_cond_initialize(rb_nativethread_cond_t *cond);
+
+/**
+ * Destroys the passed condition variable.
+ *
+ * @param[out] cond A condition variable to kill.
+ * @post `cond` is no longer eligible for other functions.
+ */
+void rb_native_cond_destroy(rb_nativethread_cond_t *cond);
+
+RBIMPL_SYMBOL_EXPORT_END()
+#endif
diff --git a/include/ruby/util.h b/include/ruby/util.h
new file mode 100644
index 0000000000..12e69c4b80
--- /dev/null
+++ b/include/ruby/util.h
@@ -0,0 +1,239 @@
+#ifndef RUBY_UTIL_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_UTIL_H 1
+/**
+ * @file
+ * @author $Author$
+ * @date Thu Mar 9 11:55:53 JST 1995
+ * @copyright Copyright (C) 1993-2007 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ * @warning DO NOT ADD RANDOM GARBAGES IN THIS FILE! Contents of this file
+ * reside here for historical reasons. Find a right place for your
+ * API!
+ */
+#include "ruby/internal/config.h"
+
+#ifdef STDC_HEADERS
+# include <stddef.h> /* size_t */
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h> /* ssize_t */
+#endif
+
+#include "ruby/internal/attr/noalias.h"
+#include "ruby/internal/attr/nodiscard.h"
+#include "ruby/internal/attr/nonnull.h"
+#include "ruby/internal/attr/restrict.h"
+#include "ruby/internal/attr/returns_nonnull.h"
+#include "ruby/internal/dllexport.h"
+#include "ruby/defines.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/** an approximation of ceil(n * log10(2)), up to 1,048,576 (1<<20)
+ * without overflow within 32-bit calculation
+ */
+#define DECIMAL_SIZE_OF_BITS(n) (((n) * 3010 + 9998) / 9999)
+
+/** an approximation of decimal representation size for n-bytes */
+#define DECIMAL_SIZE_OF_BYTES(n) DECIMAL_SIZE_OF_BITS((n) * CHAR_BIT)
+
+/**
+ * An approximation of decimal representation size. `expr` may be a
+ * type name
+ */
+#define DECIMAL_SIZE_OF(expr) DECIMAL_SIZE_OF_BYTES(sizeof(expr))
+
+/**
+ * Character to number mapping like `'a'` -> `10`, `'b'` -> `11` etc. For
+ * punctuation etc., the value is -1. "36" terminology comes from the fact
+ * that this is the table behind `str.to_i(36)`.
+ */
+RUBY_EXTERN const signed char ruby_digit36_to_number_table[];
+
+/**
+ * Characters that Ruby accepts as hexadecimal digits. This is `/\h/` expanded
+ * into an array.
+ */
+RUBY_EXTERN const char ruby_hexdigits[];
+
+/**
+ * Scans the passed string, assuming the string is a textual representation of
+ * an integer. Stops when encountering something non-digit for the passed
+ * base.
+ *
+ * @note This does not understand minus sign.
+ * @note This does not understand e.g. `0x` prefix.
+ * @note It is a failure to pass `0` to `base`, unlike ruby_strtoul().
+ * @param[in] str Target string of digits to interpret.
+ * @param[in] len Number of bytes of `str`, or -1 to detect `NUL`.
+ * @param[in] base Base, `2` to `36` inclusive.
+ * @param[out] retlen Return value buffer.
+ * @param[out] overflow Return value buffer.
+ * @return Interpreted numeric representation of `str`.
+ * @post `retlen` is the number of bytes scanned so far.
+ * @post `overflow` is set to true if the string represents something
+ * bigger than `ULONG_MAX`. Something meaningful still returns;
+ * which is the designed belabour of C's unsigned arithmetic.
+ */
+unsigned long ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow);
+
+/** @old{ruby_scan_oct} */
+#define scan_oct(s,l,e) ((int)ruby_scan_oct((s),(l),(e)))
+
+RBIMPL_ATTR_NOALIAS()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Interprets the passed string as an octal unsigned integer. Stops when
+ * encounters something not understood.
+ *
+ * @param[in] str C string to scan.
+ * @param[in] len Length of `str`.
+ * @param[out] consumed Return value buffer.
+ * @return Parsed integer.
+ * @post `ret` is the number of characters read.
+ *
+ * @internal
+ *
+ * No consideration is made for integer overflows. As the return value is
+ * unsigned this function has fully defined behaviour, but you cannot know if
+ * there was an integer wrap-around or not.
+ */
+unsigned long ruby_scan_oct(const char *str, size_t len, size_t *consumed);
+
+/** @old{ruby_scan_hex} */
+#define scan_hex(s,l,e) ((int)ruby_scan_hex((s),(l),(e)))
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Interprets the passed string a hexadecimal unsigned integer. Stops when
+ * encounters something not understood.
+ *
+ * @param[in] str C string to scan.
+ * @param[in] len Length of `str`.
+ * @param[out] ret Return value buffer.
+ * @return Parsed integer.
+ * @post `ret` is the number of characters read.
+ *
+ * @internal
+ *
+ * No consideration is made for integer overflows. As the return value is
+ * unsigned this function has fully defined behaviour, but you cannot know if
+ * there was an integer wrap-around or not.
+ */
+unsigned long ruby_scan_hex(const char *str, size_t len, size_t *ret);
+
+/**
+ * Reentrant implementation of quick sort. If your system provides something
+ * (like C11 qsort_s), this is a thin wrapper of that routine. Otherwise
+ * resorts to our own version.
+ */
+#ifdef HAVE_GNU_QSORT_R
+# define ruby_qsort qsort_r
+#else
+void ruby_qsort(void *, const size_t, const size_t,
+ int (*)(const void *, const void *, void *), void *);
+#endif
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Sets an environment variable. In case of POSIX this is a wrapper of
+ * `setenv(3)`. But there are systems which lack one. We try hard emulating.
+ *
+ * @param[in] key An environment variable.
+ * @param[in] val A value to be associated with `key`, or 0.
+ * @exception rb_eSystemCallError `setenv(3)` failed for some reason.
+ * @post Environment variable `key` is created if necessary. Its value
+ * is updated to be `val`.
+ */
+void ruby_setenv(const char *key, const char *val);
+
+RBIMPL_ATTR_NONNULL(())
+/**
+ * Deletes the passed environment variable, if any.
+ *
+ * @param[in] key An environment variable.
+ * @exception rb_eSystemCallError `unsetenv(3)` failed for some reason.
+ * @post Environment variable `key` does not exist.
+ */
+void ruby_unsetenv(const char *key);
+
+RBIMPL_ATTR_NODISCARD()
+RBIMPL_ATTR_RESTRICT()
+RBIMPL_ATTR_RETURNS_NONNULL()
+RBIMPL_ATTR_NONNULL(())
+/**
+ * This is our own version of `strdup(3)` that uses ruby_xmalloc() instead of
+ * system malloc (benefits our GC).
+ *
+ * @param[in] str Target C string to duplicate.
+ * @return An allocated C string holding the identical contents.
+ * @note Return value must be discarded using ruby_xfree().
+ */
+char *ruby_strdup(const char *str);
+
+#undef strdup
+/**
+ * @alias{ruby_strdup}
+ *
+ * @internal
+ *
+ * @shyouhei doesn't think it is a wise idea. ruby_strdup()'s return value
+ * must be passed to ruby_xfree(), but this macro makes it almost impossible.
+ */
+#define strdup(s) ruby_strdup(s)
+
+RBIMPL_ATTR_NODISCARD()
+RBIMPL_ATTR_RESTRICT()
+RBIMPL_ATTR_RETURNS_NONNULL()
+/**
+ * This is our own version of `getcwd(3)` that uses ruby_xmalloc() instead of
+ * system malloc (benefits our GC).
+ *
+ * @return An allocated C string holding the process working directory.
+ * @note Return value must be discarded using ruby_xfree().
+ */
+char *ruby_getcwd(void);
+
+RBIMPL_ATTR_NONNULL((1))
+/**
+ * Our own locale-insensitive version of `strtod(3)`. The conversion is done
+ * as if the current locale is set to the "C" locale, no matter actual runtime
+ * locale settings.
+ *
+ * @param[in] str Decimal or hexadecimal representation of a floating
+ * point number.
+ * @param[out] endptr NULL, or an arbitrary pointer (overwritten on return).
+ * @return Converted number.
+ * @post If `endptr` is not NULL, it is updated to point the first such
+ * byte where conversion failed.
+ * @note This function sets `errno` on failure.
+ * - `ERANGE`: Converted integer is out of range of `double`.
+ * @see William D. Clinger, "How to Read Floating Point Numbers
+ * Accurately" in Proc. ACM SIGPLAN '90, pp. 92-101.
+ * https://doi.org/10.1145/93542.93557
+ */
+double ruby_strtod(const char *str, char **endptr);
+
+#undef strtod
+/** @alias{ruby_strtod} */
+#define strtod(s,e) ruby_strtod((s),(e))
+
+RBIMPL_ATTR_NONNULL((2))
+/**
+ * Scans the passed string, with calling the callback function every time it
+ * encounters a "word". A word here is a series of characters separated by
+ * either a space (of IEEE 1003.1 section 7.3.1.1), or a `','`.
+ *
+ * @param[in] str Target string to split into each words.
+ * @param[in] func Callback function.
+ * @param[in,out] argv Passed as-is to `func`.
+ */
+void ruby_each_words(const char *str, void (*func)(const char *word, int len, void *argv), void *argv);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_UTIL_H */
diff --git a/include/ruby/version.h b/include/ruby/version.h
new file mode 100644
index 0000000000..5bb381cea2
--- /dev/null
+++ b/include/ruby/version.h
@@ -0,0 +1,159 @@
+#ifndef RUBY_VERSION_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_VERSION_H 1
+/**
+ * @file
+ * @author $Author$
+ * @date Wed May 13 12:56:56 JST 2009
+ * @copyright Copyright (C) 1993-2009 Yukihiro Matsumoto
+ * @copyright Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ * @copyright Copyright (C) 2000 Information-technology Promotion Agency, Japan
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ *
+ * This file contains only
+ * - never-changeable information, and
+ * - interfaces accessible from extension libraries.
+ *
+ * Never try to check RUBY_VERSION_CODE etc in extension libraries,
+ * check the features with mkmf.rb instead.
+ */
+
+/**
+ * @name The origin.
+ *
+ * These information never change. Just written here to remember.
+ *
+ * @{
+ */
+
+/** Author of this project. */
+#define RUBY_AUTHOR "Yukihiro Matsumoto"
+
+/** Ruby's birth year. */
+#define RUBY_BIRTH_YEAR 1993
+
+/** Ruby's birth month. */
+#define RUBY_BIRTH_MONTH 2
+
+/** Ruby's birth day. */
+#define RUBY_BIRTH_DAY 24
+
+/** @} */
+
+/**
+ * @name The API version.
+ *
+ * API version is different from binary version. These numbers are for API
+ * stability. When you have distinct API versions x and y, you cannot expect
+ * codes targeted to x also works for y.
+ *
+ * However let us repeat here that it's a BAD idea to check
+ * #RUBY_API_VERSION_CODE form extension libraries. Different API versions are
+ * just different. There is no such thing like upper compatibility.
+ *
+ * @{
+ */
+
+/**
+ * Major version. This digit changes sometimes for various reasons, but that
+ * doesn't mean a total rewrite. Practically when it comes to API versioning,
+ * major and minor version changes are equally catastrophic.
+ */
+#define RUBY_API_VERSION_MAJOR 4
+
+/**
+ * Minor version. As of writing this version changes annually. Greater
+ * version doesn't mean "better"; they just mean years passed.
+ */
+#define RUBY_API_VERSION_MINOR 1
+
+/**
+ * Teeny version. This digit is kind of reserved these days. Kept 0 for the
+ * entire 2.x era. Waiting for future uses.
+ */
+#define RUBY_API_VERSION_TEENY 0
+
+/**
+ * This macro is API versions encoded into a C integer.
+ *
+ * @note Use mkmf.
+ * @note Don't rely on it.
+ */
+#define RUBY_API_VERSION_CODE (RUBY_API_VERSION_MAJOR*10000+RUBY_API_VERSION_MINOR*100+RUBY_API_VERSION_TEENY)
+
+/** @} */
+
+#ifdef RUBY_EXTERN
+/* Internal note: this file could be included from verconf.mk _before_
+ * generating config.h, on Windows. The #ifdef above is to trick such
+ * situation. */
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * @name Interfaces from extension libraries.
+ *
+ * Before using these infos, think thrice whether they are really
+ * necessary or not, and if the answer was yes, think twice a week
+ * later again.
+ *
+ * @{
+ */
+
+/** API versions, in { major, minor, teeny } order. */
+RUBY_EXTERN const int ruby_api_version[3];
+
+/**
+ * Stringised version.
+ *
+ * @note This is the runtime version, not the API version. For instance it
+ * was `"2.5.9"` when ::ruby_api_version was `{ 2, 5, 0 }`.
+ */
+RUBY_EXTERN const char ruby_version[];
+
+/** Date of release, in a C string. */
+RUBY_EXTERN const char ruby_release_date[];
+
+/**
+ * Target platform identifier, in a C string.
+ *
+ * @note Seasoned UNIX programmers should beware that this "platform
+ * identifier" is our invention; not always identical to so-called
+ * target triplets that GNU systems use. For instance on @shyouhei's
+ * machine, ::ruby_platform is `"x64_64-linux"` while its target triplet
+ * is `x86_64-pc-linux-gnu`.
+ * @note Note also that we support Windows.
+ */
+RUBY_EXTERN const char ruby_platform[];
+
+/**
+ * This is a monotonic increasing integer that describes specific "patch"
+ * level. You can know the exact changeset your binary is running by this info
+ * (and ::ruby_version), unless this is -1. -1 means there is no release yet
+ * for the version; ruby is actively developed. 0 means the initial GA version.
+ */
+RUBY_EXTERN const int ruby_patchlevel;
+
+/**
+ * This is what `ruby -v` prints to the standard error. Something like:
+ * `"ruby 2.5.9p229 (2021-04-05 revision 67829) [x86_64-linux]"`. This doesn't
+ * include runtime options like a JIT being enabled.
+ */
+RUBY_EXTERN const char ruby_description[];
+
+/** Copyright notice. */
+RUBY_EXTERN const char ruby_copyright[];
+
+/**
+ * This is just `"ruby"` for us. But different implementations can have
+ * different strings here.
+ */
+RUBY_EXTERN const char ruby_engine[];
+
+/** @} */
+
+RBIMPL_SYMBOL_EXPORT_END()
+#endif
+
+#endif
diff --git a/include/ruby/vm.h b/include/ruby/vm.h
new file mode 100644
index 0000000000..8779780952
--- /dev/null
+++ b/include/ruby/vm.h
@@ -0,0 +1,61 @@
+#ifndef RUBY_VM_H /*-*-C++-*-vi:se ft=cpp:*/
+#define RUBY_VM_H 1
+/**
+ * @file
+ * @author $Author$
+ * @date Sat May 31 15:17:36 2008
+ * @copyright Copyright (C) 2008 Yukihiro Matsumoto
+ * @copyright This file is a part of the programming language Ruby.
+ * Permission is hereby granted, to either redistribute and/or
+ * modify this file, provided that the conditions mentioned in the
+ * file COPYING are met. Consult the file for details.
+ *
+ * We planned to have multiple VMs run side-by-side. The API here was a
+ * preparation of that feature. The topic branch was eventually abandoned, and
+ * we now have Ractor. This file is kind of obsolescent.
+ */
+#include "ruby/internal/dllexport.h"
+
+RBIMPL_SYMBOL_EXPORT_BEGIN()
+
+/**
+ * The opaque struct to hold VM internals. Its fields are intentionally hidden
+ * from extension libraries because it changes drastically time to time.
+ */
+typedef struct rb_vm_struct ruby_vm_t;
+
+/**
+ * Destructs the passed VM. You don't have to call this API directly now,
+ * because there is no way to create one. There is only one VM at one time.
+ * ruby_stop() should just suffice.
+ */
+int ruby_vm_destruct(ruby_vm_t *vm);
+
+/**
+ * ruby_vm_at_exit registers a function _func_ to be invoked when a VM
+ * passed away. Functions registered this way runs in reverse order
+ * of registration, just like END {} block does. The difference is
+ * its timing to be triggered. ruby_vm_at_exit functions runs when a
+ * VM _passed_ _away_, while END {} blocks runs just _before_ a VM
+ * _is_ _passing_ _away_.
+ *
+ * You cannot register a function to another VM than where you are in.
+ * So where to register is intuitive, omitted. OTOH the argument
+ * _func_ cannot know which VM it is in because at the time of
+ * invocation, the VM has already died and there is no execution
+ * context. The VM itself is passed as the first argument to it.
+ *
+ * @param[in] func the function to register.
+ */
+void ruby_vm_at_exit(void(*func)(ruby_vm_t *));
+
+/**
+ * Returns whether the Ruby VM will free all memory at shutdown.
+ *
+ * @return true if free-at-exit is enabled, false otherwise.
+ */
+bool ruby_free_at_exit_p(void);
+
+RBIMPL_SYMBOL_EXPORT_END()
+
+#endif /* RUBY_VM_H */
diff --git a/include/ruby/win32.h b/include/ruby/win32.h
new file mode 100644
index 0000000000..ae11a61481
--- /dev/null
+++ b/include/ruby/win32.h
@@ -0,0 +1,841 @@
+#ifndef RUBY_WIN32_H
+#define RUBY_WIN32_H 1
+
+#if defined(__cplusplus)
+extern "C" {
+#if 0
+} /* satisfy cc-mode */
+#endif
+#endif
+
+RUBY_SYMBOL_EXPORT_BEGIN
+
+/*
+ * Copyright (c) 1993, Intergraph Corporation
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the perl README file.
+ *
+ */
+
+/*
+ * Ok now we can include the normal include files.
+ */
+
+/* #include <stdarg.h> conflict with varargs.h? */
+#if !defined(WSAAPI)
+#if defined(__cplusplus) && defined(_MSC_VER)
+extern "C++" { /* template without extern "C++" */
+#endif
+#if !defined(_WIN64) && !defined(WIN32)
+#define WIN32
+#endif
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <mswsock.h>
+#include <iphlpapi.h>
+#if defined(__cplusplus) && defined(_MSC_VER)
+}
+#endif
+#endif
+
+/*
+ * We're not using Microsoft's "extensions" to C for
+ * Structured Exception Handling (SEH) so we can nuke these
+ */
+#undef try
+#undef except
+#undef finally
+#undef leave
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <direct.h>
+#include <process.h>
+#include <time.h>
+#include <math.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_UTIME_H
+# include <sys/utime.h>
+#else
+# include <utime.h>
+#endif
+#include <io.h>
+#include <malloc.h>
+#if defined __MINGW32__
+# include <stdint.h>
+#else
+# if !defined(_INTPTR_T_DEFINED)
+# ifdef _WIN64
+typedef __int64 intptr_t;
+# else
+typedef int intptr_t;
+# endif
+# define _INTPTR_T_DEFINED
+# endif
+# if !defined(INTPTR_MAX)
+# ifdef _WIN64
+# define INTPTR_MAX 9223372036854775807I64
+# else
+# define INTPTR_MAX 2147483647
+# endif
+# define INTPTR_MIN (-INTPTR_MAX-1)
+# endif
+# if !defined(_UINTPTR_T_DEFINED)
+# ifdef _WIN64
+typedef unsigned __int64 uintptr_t;
+# else
+typedef unsigned int uintptr_t;
+# endif
+# define _UINTPTR_T_DEFINED
+# endif
+# if !defined(UINTPTR_MAX)
+# ifdef _WIN64
+# define UINTPTR_MAX 18446744073709551615UI64
+# else
+# define UINTPTR_MAX 4294967295U
+# endif
+# endif
+#endif
+#ifndef __MINGW32__
+# define mode_t int
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#define rb_w32_iswinnt() TRUE
+#define rb_w32_iswin95() FALSE
+
+#define WNOHANG -1
+
+#define O_SHARE_DELETE 0x20000000 /* for rb_w32_open(), rb_w32_wopen() */
+
+typedef int clockid_t;
+
+/*
+ * Since we use our versions in win32/win32.c, not to depend on yet
+ * another DLL, prefix our versions not to conflict with inline
+ * versions provided in time.h.
+ */
+#define clock_gettime rb_w32_clock_gettime
+#define clock_getres rb_w32_clock_getres
+
+#ifndef CLOCK_REALTIME
+# define CLOCK_REALTIME 0
+#endif
+#ifndef CLOCK_MONOTONIC
+# define CLOCK_MONOTONIC 1
+#endif
+#ifndef CLOCK_PROCESS_CPUTIME_ID
+# define CLOCK_PROCESS_CPUTIME_ID 2
+#endif
+#ifndef CLOCK_THREAD_CPUTIME_ID
+# define CLOCK_THREAD_CPUTIME_ID 3
+#endif
+#ifndef CLOCK_REALTIME_COARSE
+# define CLOCK_REALTIME_COARSE 4
+#endif
+
+#undef utime
+#undef lseek
+#undef stat
+#undef fstat
+#ifdef RUBY_EXPORT
+#define utime(_p, _t) rb_w32_uutime(_p, _t)
+#undef HAVE_UTIMES
+#define HAVE_UTIMES 1
+#define utimes(_p, _t) rb_w32_uutimes(_p, _t)
+#undef HAVE_UTIMENSAT
+#define HAVE_UTIMENSAT 1
+#define AT_FDCWD -100
+#define utimensat(_d, _p, _t, _f) rb_w32_uutimensat(_d, _p, _t, _f)
+#define lseek(_f, _o, _w) rb_w32_lseek(_f, _o, _w)
+
+#define pipe(p) rb_w32_pipe(p)
+#define open rb_w32_uopen
+#define close(h) rb_w32_close(h)
+#define fclose(f) rb_w32_fclose(f)
+#define read(f, b, s) rb_w32_read(f, b, s)
+#define write(f, b, s) rb_w32_write(f, b, s)
+#define pread(f, b, s, o) rb_w32_pread(f, b, s, o)
+#define pwrite(f, b, s, o) rb_w32_pwrite(f, b, s, o)
+#define getpid() rb_w32_getpid()
+#undef HAVE_GETPPID
+#define HAVE_GETPPID 1
+#define getppid() rb_w32_getppid()
+#define sleep(x) rb_w32_Sleep((x)*1000)
+#define Sleep(msec) (void)rb_w32_Sleep(msec)
+
+#undef HAVE_EXECV
+#define HAVE_EXECV 1
+#undef execv
+#define execv(path,argv) rb_w32_uaspawn(P_OVERLAY,path,argv)
+#undef isatty
+#define isatty(h) rb_w32_isatty(h)
+
+#undef mkdir
+#define mkdir(p, m) rb_w32_umkdir(p, m)
+#undef rmdir
+#define rmdir(p) rb_w32_urmdir(p)
+#undef unlink
+#define unlink(p) rb_w32_uunlink(p)
+#endif /* RUBY_EXPORT */
+
+/* same with stati64 except the size of st_ino and nanosecond timestamps */
+struct stati128 {
+ _dev_t st_dev;
+ unsigned __int64 st_ino;
+ __int64 st_inohigh;
+ unsigned short st_mode;
+ short st_nlink;
+ short st_uid;
+ short st_gid;
+ _dev_t st_rdev;
+ __int64 st_size;
+ __time64_t st_atime;
+ long st_atimensec;
+ __time64_t st_mtime;
+ long st_mtimensec;
+ __time64_t st_ctime;
+ long st_ctimensec;
+};
+
+#define stat stati128
+#undef SIZEOF_STRUCT_STAT_ST_INO
+#define SIZEOF_STRUCT_STAT_ST_INO sizeof(unsigned __int64)
+#define HAVE_STRUCT_STAT_ST_INOHIGH
+#define HAVE_STRUCT_STAT_ST_ATIMENSEC
+#define HAVE_STRUCT_STAT_ST_MTIMENSEC
+#define HAVE_STRUCT_STAT_ST_CTIMENSEC
+#define fstat(fd,st) rb_w32_fstati128(fd,st)
+#define stati128(path, st) rb_w32_ustati128(path,st)
+#define lstat(path,st) rb_w32_ulstati128(path,st)
+#define access(path,mode) rb_w32_uaccess(path,mode)
+
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define fsync _commit
+
+struct timezone;
+
+#ifdef __MINGW32__
+#undef isascii
+#define isascii __isascii
+#endif
+
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+struct msghdr {
+ void *msg_name;
+ int msg_namelen;
+ struct iovec *msg_iov;
+ int msg_iovlen;
+ void *msg_control;
+ int msg_controllen;
+ int msg_flags;
+};
+
+/* for getifaddrs() and others */
+struct ifaddrs {
+ struct ifaddrs *ifa_next;
+ char *ifa_name;
+ u_int ifa_flags;
+ struct sockaddr *ifa_addr;
+ struct sockaddr *ifa_netmask;
+ struct sockaddr *ifa_broadaddr;
+ struct sockaddr *ifa_dstaddr;
+ void *ifa_data;
+};
+#ifdef IF_NAMESIZE
+#define IFNAMSIZ IF_NAMESIZE
+#else
+#define IFNAMSIZ 256
+#endif
+#ifdef IFF_POINTTOPOINT
+#define IFF_POINTOPOINT IFF_POINTTOPOINT
+#endif
+
+extern void rb_w32_sysinit(int *, char ***);
+extern int flock(int fd, int oper);
+extern int rb_w32_io_cancelable_p(int);
+extern int rb_w32_is_socket(int);
+extern int WSAAPI rb_w32_accept(int, struct sockaddr *, int *);
+extern int WSAAPI rb_w32_bind(int, const struct sockaddr *, int);
+extern int WSAAPI rb_w32_connect(int, const struct sockaddr *, int);
+extern void rb_w32_fdset(int, fd_set*);
+extern void rb_w32_fdclr(int, fd_set*);
+extern int rb_w32_fdisset(int, fd_set*);
+extern int WSAAPI rb_w32_select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+extern int WSAAPI rb_w32_getpeername(int, struct sockaddr *, int *);
+extern int WSAAPI rb_w32_getsockname(int, struct sockaddr *, int *);
+extern int WSAAPI rb_w32_getsockopt(int, int, int, char *, int *);
+extern int WSAAPI rb_w32_ioctlsocket(int, long, u_long *);
+extern int WSAAPI rb_w32_listen(int, int);
+extern int WSAAPI rb_w32_recv(int, char *, int, int);
+extern int WSAAPI rb_w32_recvfrom(int, char *, int, int, struct sockaddr *, int *);
+extern int WSAAPI rb_w32_send(int, const char *, int, int);
+extern int WSAAPI rb_w32_sendto(int, const char *, int, int, const struct sockaddr *, int);
+extern int recvmsg(int, struct msghdr *, int);
+extern int sendmsg(int, const struct msghdr *, int);
+extern int WSAAPI rb_w32_setsockopt(int, int, int, const char *, int);
+extern int WSAAPI rb_w32_shutdown(int, int);
+extern int WSAAPI rb_w32_socket(int, int, int);
+extern SOCKET rb_w32_get_osfhandle(int);
+extern struct hostent *WSAAPI rb_w32_gethostbyaddr(const char *, int, int);
+extern struct hostent *WSAAPI rb_w32_gethostbyname(const char *);
+extern int WSAAPI rb_w32_gethostname(char *, int);
+extern struct protoent *WSAAPI rb_w32_getprotobyname(const char *);
+extern struct protoent *WSAAPI rb_w32_getprotobynumber(int);
+extern struct servent *WSAAPI rb_w32_getservbyname(const char *, const char *);
+extern struct servent *WSAAPI rb_w32_getservbyport(int, const char *);
+extern int socketpair(int, int, int, int *);
+extern int getifaddrs(struct ifaddrs **);
+extern void freeifaddrs(struct ifaddrs *);
+extern char * rb_w32_ugetcwd(char *, int);
+extern char * rb_w32_ugetenv(const char *);
+extern int rb_w32_urename(const char *, const char *);
+extern char **rb_w32_get_environ(void);
+extern void rb_w32_free_environ(char **);
+extern int rb_w32_map_errno(DWORD);
+extern const char *WSAAPI rb_w32_inet_ntop(int,const void *,char *,size_t);
+extern int WSAAPI rb_w32_inet_pton(int,const char *,void *);
+
+RBIMPL_ATTR_DEPRECATED(("as Windows 9x is not supported already"))
+static inline DWORD rb_w32_osid(void) {return VER_PLATFORM_WIN32_NT;}
+RBIMPL_ATTR_DEPRECATED(("by Windows Version Helper APIs"))
+extern DWORD rb_w32_osver(void);
+
+extern int rb_w32_uchown(const char *, int, int);
+extern int rb_w32_ulink(const char *, const char *);
+extern ssize_t rb_w32_ureadlink(const char *, char *, size_t);
+extern int rb_w32_usymlink(const char *src, const char *link);
+extern int gettimeofday(struct timeval *, struct timezone *);
+extern int clock_gettime(clockid_t, struct timespec *);
+extern int clock_getres(clockid_t, struct timespec *);
+extern rb_pid_t waitpid(rb_pid_t, int *, int);
+extern rb_pid_t wait(int *);
+extern rb_pid_t rb_w32_uspawn(int, const char *, const char*);
+extern rb_pid_t rb_w32_uaspawn(int, const char *, char *const *);
+extern rb_pid_t rb_w32_uaspawn_flags(int, const char *, char *const *, DWORD);
+#undef HAVE_KILL
+#define HAVE_KILL 1
+extern int kill(rb_pid_t, int);
+extern int fcntl(int, int, ...);
+extern int rb_w32_set_nonblock(int);
+extern rb_pid_t rb_w32_getpid(void);
+extern rb_pid_t rb_w32_getppid(void);
+extern int rb_w32_isatty(int);
+extern int rb_w32_uchdir(const char *);
+extern int rb_w32_umkdir(const char *, int);
+extern int rb_w32_urmdir(const char *);
+extern int rb_w32_uunlink(const char *);
+extern int rb_w32_uchmod(const char *, int);
+extern int rb_w32_ustati128(const char *, struct stati128 *);
+extern int rb_w32_ulstati128(const char *, struct stati128 *);
+extern int rb_w32_uaccess(const char *, int);
+extern char rb_w32_fd_is_text(int);
+extern int rb_w32_fstati128(int, struct stati128 *);
+extern int rb_w32_dup2(int, int);
+
+#include <float.h>
+
+#if defined _MSC_VER && defined INFINITY
+#pragma warning(push)
+#pragma warning(disable:4756)
+static inline float
+rb_infinity_float(void)
+{
+ return INFINITY;
+}
+#pragma warning(pop)
+#undef INFINITY
+#define INFINITY rb_infinity_float()
+#endif
+
+#if !defined __MINGW32__ || defined __NO_ISOCEXT
+#ifndef copysign
+#define copysign(a, b) _copysign(a, b)
+#endif
+static inline double
+scalb(double a, long b)
+{
+ return _scalb(a, b);
+}
+#endif
+
+#if !defined S_IFIFO && defined _S_IFIFO
+#define S_IFIFO _S_IFIFO
+#endif
+
+#if !defined S_IRUSR && !defined __MINGW32__
+#define S_IRUSR 0400
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 0040
+#endif
+#ifndef S_IROTH
+#define S_IROTH 0004
+#endif
+
+#if !defined S_IWUSR && !defined __MINGW32__
+#define S_IWUSR 0200
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP 0020
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 0002
+#endif
+
+#if !defined S_IXUSR && !defined __MINGW32__
+#define S_IXUSR 0100
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP 0010
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH 0001
+#endif
+
+#define S_IFLNK 0xa000
+#define S_IFSOCK 0xc000
+
+/*
+ * define this so we can do inplace editing
+ */
+
+#define SUFFIX
+
+extern int rb_w32_ftruncate(int fd, rb_off_t length);
+extern int rb_w32_truncate(const char *path, rb_off_t length);
+extern int rb_w32_utruncate(const char *path, rb_off_t length);
+
+#undef HAVE_FTRUNCATE
+#define HAVE_FTRUNCATE 1
+#if defined HAVE_FTRUNCATE64
+#define ftruncate ftruncate64
+#else
+#define ftruncate rb_w32_ftruncate
+#endif
+
+#undef HAVE_TRUNCATE
+#define HAVE_TRUNCATE 1
+#define truncate rb_w32_utruncate
+
+/*
+ * stubs
+ */
+extern int ioctl (int, int, ...);
+extern rb_uid_t getuid (void);
+extern rb_uid_t geteuid (void);
+extern rb_gid_t getgid (void);
+extern rb_gid_t getegid (void);
+extern int setuid (rb_uid_t);
+extern int setgid (rb_gid_t);
+
+extern char *rb_w32_strerror(int);
+
+#ifdef RUBY_EXPORT
+#define strerror(e) rb_w32_strerror(e)
+#endif
+
+#define PIPE_BUF 1024
+
+#define LOCK_SH 1
+#define LOCK_EX 2
+#define LOCK_NB 4
+#define LOCK_UN 8
+
+
+#ifndef SIGINT
+#define SIGINT 2
+#endif
+#ifndef SIGKILL
+#define SIGKILL 9
+#endif
+
+
+/* #undef va_start */
+/* #undef va_end */
+
+/* winsock error map */
+#include <errno.h>
+
+#ifndef EWOULDBLOCK
+# define EWOULDBLOCK WSAEWOULDBLOCK
+#endif
+#ifndef EINPROGRESS
+# define EINPROGRESS WSAEINPROGRESS
+#endif
+#ifndef EALREADY
+# define EALREADY WSAEALREADY
+#endif
+#ifndef ENOTSOCK
+# define ENOTSOCK WSAENOTSOCK
+#endif
+#ifndef EDESTADDRREQ
+# define EDESTADDRREQ WSAEDESTADDRREQ
+#endif
+#ifndef EMSGSIZE
+# define EMSGSIZE WSAEMSGSIZE
+#endif
+#ifndef EPROTOTYPE
+# define EPROTOTYPE WSAEPROTOTYPE
+#endif
+#ifndef ENOPROTOOPT
+# define ENOPROTOOPT WSAENOPROTOOPT
+#endif
+#ifndef EPROTONOSUPPORT
+# define EPROTONOSUPPORT WSAEPROTONOSUPPORT
+#endif
+#ifndef ESOCKTNOSUPPORT
+# define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
+#endif
+#ifndef EOPNOTSUPP
+# define EOPNOTSUPP WSAEOPNOTSUPP
+#endif
+#ifndef EPFNOSUPPORT
+# define EPFNOSUPPORT WSAEPFNOSUPPORT
+#endif
+#ifndef EAFNOSUPPORT
+# define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+#ifndef EADDRINUSE
+# define EADDRINUSE WSAEADDRINUSE
+#endif
+#ifndef EADDRNOTAVAIL
+# define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#endif
+#ifndef ENETDOWN
+# define ENETDOWN WSAENETDOWN
+#endif
+#ifndef ENETUNREACH
+# define ENETUNREACH WSAENETUNREACH
+#endif
+#ifndef ENETRESET
+# define ENETRESET WSAENETRESET
+#endif
+#ifndef ECONNABORTED
+# define ECONNABORTED WSAECONNABORTED
+#endif
+#ifndef ECONNRESET
+# define ECONNRESET WSAECONNRESET
+#endif
+#ifndef ENOBUFS
+# define ENOBUFS WSAENOBUFS
+#endif
+#ifndef EISCONN
+# define EISCONN WSAEISCONN
+#endif
+#ifndef ENOTCONN
+# define ENOTCONN WSAENOTCONN
+#endif
+#ifndef ESHUTDOWN
+# define ESHUTDOWN WSAESHUTDOWN
+#endif
+#ifndef ETOOMANYREFS
+# define ETOOMANYREFS WSAETOOMANYREFS
+#endif
+#ifndef ETIMEDOUT
+# define ETIMEDOUT WSAETIMEDOUT
+#endif
+#ifndef ECONNREFUSED
+# define ECONNREFUSED WSAECONNREFUSED
+#endif
+#ifndef ELOOP
+# define ELOOP WSAELOOP
+#endif
+/*#define ENAMETOOLONG WSAENAMETOOLONG*/
+#ifndef EHOSTDOWN
+# define EHOSTDOWN WSAEHOSTDOWN
+#endif
+#ifndef EHOSTUNREACH
+# define EHOSTUNREACH WSAEHOSTUNREACH
+#endif
+/*#define ENOTEMPTY WSAENOTEMPTY*/
+#ifndef EPROCLIM
+# define EPROCLIM WSAEPROCLIM
+#endif
+#ifndef EUSERS
+# define EUSERS WSAEUSERS
+#endif
+#ifndef EDQUOT
+# define EDQUOT WSAEDQUOT
+#endif
+#ifndef ESTALE
+# define ESTALE WSAESTALE
+#endif
+#ifndef EREMOTE
+# define EREMOTE WSAEREMOTE
+#endif
+
+#define F_DUPFD 0
+#define F_GETFD 1
+#define F_SETFD 2
+#if 0
+#define F_GETFL 3
+#endif
+#define F_SETFL 4
+#define F_DUPFD_CLOEXEC 67
+#define FD_CLOEXEC 1 /* F_GETFD, F_SETFD */
+#define O_NONBLOCK 1
+
+#undef FD_SET
+#define FD_SET(fd, set) do {\
+ unsigned int i;\
+ SOCKET s = _get_osfhandle(fd);\
+\
+ for (i = 0; i < (set)->fd_count; i++) {\
+ if ((set)->fd_array[i] == s) {\
+ break;\
+ }\
+ }\
+ if (i == (set)->fd_count) {\
+ if ((set)->fd_count < FD_SETSIZE) {\
+ (set)->fd_array[i] = s;\
+ (set)->fd_count++;\
+ }\
+ }\
+} while(0)
+
+#undef FD_CLR
+#define FD_CLR(f, s) rb_w32_fdclr(f, s)
+
+#undef FD_ISSET
+#define FD_ISSET(f, s) rb_w32_fdisset(f, s)
+
+#ifdef RUBY_EXPORT
+#undef inet_ntop
+#define inet_ntop(f,a,n,l) rb_w32_inet_ntop(f,a,n,l)
+
+#undef inet_pton
+#define inet_pton(f,s,d) rb_w32_inet_pton(f,s,d)
+
+#undef accept
+#define accept(s, a, l) rb_w32_accept(s, a, l)
+
+#undef bind
+#define bind(s, a, l) rb_w32_bind(s, a, l)
+
+#undef connect
+#define connect(s, a, l) rb_w32_connect(s, a, l)
+
+#undef select
+#define select(n, r, w, e, t) rb_w32_select(n, r, w, e, t)
+
+#undef getpeername
+#define getpeername(s, a, l) rb_w32_getpeername(s, a, l)
+
+#undef getsockname
+#define getsockname(s, a, l) rb_w32_getsockname(s, a, l)
+
+#undef getsockopt
+#define getsockopt(s, v, n, o, l) rb_w32_getsockopt(s, v, n, o, l)
+
+#undef ioctlsocket
+#define ioctlsocket(s, c, a) rb_w32_ioctlsocket(s, c, a)
+
+#undef listen
+#define listen(s, b) rb_w32_listen(s, b)
+
+#undef recv
+#define recv(s, b, l, f) rb_w32_recv(s, b, l, f)
+
+#undef recvfrom
+#define recvfrom(s, b, l, f, fr, frl) rb_w32_recvfrom(s, b, l, f, fr, frl)
+
+#undef send
+#define send(s, b, l, f) rb_w32_send(s, b, l, f)
+
+#undef sendto
+#define sendto(s, b, l, f, t, tl) rb_w32_sendto(s, b, l, f, t, tl)
+
+#undef setsockopt
+#define setsockopt(s, v, n, o, l) rb_w32_setsockopt(s, v, n, o, l)
+
+#undef HAVE_SHUTDOWN
+#define HAVE_SHUTDOWN 1
+#undef shutdown
+#define shutdown(s, h) rb_w32_shutdown(s, h)
+
+#undef socket
+#define socket(s, t, p) rb_w32_socket(s, t, p)
+
+#undef gethostbyaddr
+#define gethostbyaddr(a, l, t) rb_w32_gethostbyaddr(a, l, t)
+
+#undef gethostbyname
+#define gethostbyname(n) rb_w32_gethostbyname(n)
+
+#undef gethostname
+#define gethostname(n, l) rb_w32_gethostname(n, l)
+
+#undef getprotobyname
+#define getprotobyname(n) rb_w32_getprotobyname(n)
+
+#undef getprotobynumber
+#define getprotobynumber(n) rb_w32_getprotobynumber(n)
+
+#undef getservbyname
+#define getservbyname(n, p) rb_w32_getservbyname(n, p)
+
+#undef getservbyport
+#define getservbyport(p, pr) rb_w32_getservbyport(p, pr)
+
+#undef get_osfhandle
+#define get_osfhandle(h) rb_w32_get_osfhandle(h)
+
+#undef getcwd
+#define getcwd(b, s) rb_w32_ugetcwd(b, s)
+
+#undef getenv
+#define getenv(n) rb_w32_ugetenv(n)
+
+#undef rename
+#define rename(o, n) rb_w32_urename(o, n)
+
+#undef times
+#define times(t) rb_w32_times(t)
+
+#undef dup2
+#define dup2(o, n) rb_w32_dup2(o, n)
+#endif
+
+struct tms {
+ long tms_utime;
+ long tms_stime;
+ long tms_cutime;
+ long tms_cstime;
+};
+
+int rb_w32_times(struct tms *);
+
+struct tm *gmtime_r(const time_t *, struct tm *);
+struct tm *localtime_r(const time_t *, struct tm *);
+
+/* thread stuff */
+int rb_w32_sleep(unsigned long msec);
+int rb_w32_uopen(const char *, int, ...);
+int rb_w32_wopen(const WCHAR *, int, ...);
+int rb_w32_close(int);
+int rb_w32_fclose(FILE*);
+int rb_w32_pipe(int[2]);
+ssize_t rb_w32_read(int, void *, size_t);
+ssize_t rb_w32_write(int, const void *, size_t);
+ssize_t rb_w32_pread(int, void *, size_t, rb_off_t offset);
+ssize_t rb_w32_pwrite(int, const void *, size_t, rb_off_t offset);
+rb_off_t rb_w32_lseek(int, rb_off_t, int);
+int rb_w32_uutime(const char *, const struct utimbuf *);
+int rb_w32_uutimes(const char *, const struct timeval *);
+int rb_w32_uutimensat(int /* must be AT_FDCWD */, const char *, const struct timespec *, int /* must be 0 */);
+long rb_w32_write_console(uintptr_t, int); /* use uintptr_t instead of VALUE because it's not defined yet here */
+int WINAPI rb_w32_Sleep(unsigned long msec);
+int rb_w32_wait_events_blocking(HANDLE *events, int num, DWORD timeout);
+int rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait);
+int rb_w32_wrap_io_handle(HANDLE, int);
+int rb_w32_unwrap_io_handle(int);
+WCHAR *rb_w32_mbstr_to_wstr(UINT, const char *, int, long *);
+char *rb_w32_wstr_to_mbstr(UINT, const WCHAR *, int, long *);
+
+DEPRECATED_BY(rb_w32_ugetcwd, char *rb_w32_getcwd(char *, int));
+DEPRECATED_BY(rb_w32_ugetenv, char *rb_w32_getenv(const char *));
+DEPRECATED_BY(rb_w32_urename, int rb_w32_rename(const char *, const char *));
+DEPRECATED_BY(rb_w32_uopen, int rb_w32_open(const char *, int, ...));
+DEPRECATED_BY(rb_w32_uchown, int chown(const char *, int, int));
+DEPRECATED_BY(rb_w32_ulink, int link(const char *, const char *));
+DEPRECATED_BY(rb_w32_ureadlink, ssize_t readlink(const char *, char *, size_t));
+DEPRECATED_BY(rb_w32_usymlink, int symlink(const char *src, const char *link));
+DEPRECATED_BY(rb_w32_umkdir, int rb_w32_mkdir(const char *, int));
+DEPRECATED_BY(rb_w32_urmdir, int rb_w32_rmdir(const char *));
+DEPRECATED_BY(rb_w32_uunlink, int rb_w32_unlink(const char *));
+DEPRECATED_BY(rb_w32_uutime, int rb_w32_utime(const char *, const struct utimbuf *));
+DEPRECATED_BY(rb_w32_uutimes, int rb_w32_utimes(const char *, const struct timeval *));
+DEPRECATED_BY(rb_w32_uutimensat, int rb_w32_utimensat(int, const char *, const struct timespec *, int));
+DEPRECATED_BY(rb_w32_ustati128, int rb_w32_stati128(const char *, struct stati128 *));
+DEPRECATED_BY(rb_w32_ulstati128, int rb_w32_lstati128(const char *, struct stati128 *));
+DEPRECATED_BY(rb_w32_uaccess, int rb_w32_access(const char *, int));
+DEPRECATED_BY(rb_w32_uspawn, rb_pid_t rb_w32_spawn(int, const char *, const char*));
+DEPRECATED_BY(rb_w32_uaspawn, rb_pid_t rb_w32_aspawn(int, const char *, char *const *));
+DEPRECATED_BY(rb_w32_uaspawn_flags, rb_pid_t rb_w32_aspawn_flags(int, const char *, char *const *, DWORD));
+
+/*
+== ***CAUTION***
+Since this function is very dangerous, ((*NEVER*))
+* lock any HANDLEs(i.e. Mutex, Semaphore, CriticalSection and so on) or,
+* use anything like rb_thread_call_without_gvl,
+in asynchronous_func_t.
+*/
+typedef uintptr_t (*asynchronous_func_t)(uintptr_t self, int argc, uintptr_t* argv);
+uintptr_t rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self, int argc, uintptr_t* argv, uintptr_t intrval);
+
+RUBY_SYMBOL_EXPORT_END
+
+#if (defined(__MINGW64_VERSION_MAJOR) || defined(__MINGW64__)) && !defined(__cplusplus)
+#ifdef RUBY_MINGW64_BROKEN_FREXP_MODF
+/* License: Ruby's */
+/* get rid of bugs in math.h of mingw */
+#define frexp(_X, _Y) __extension__ ({\
+ int intpart_frexp_bug = intpart_frexp_bug;\
+ double result_frexp_bug = frexp((_X), &intpart_frexp_bug);\
+ *(_Y) = intpart_frexp_bug;\
+ result_frexp_bug;\
+})
+/* License: Ruby's */
+#define modf(_X, _Y) __extension__ ({\
+ double intpart_modf_bug = intpart_modf_bug;\
+ double result_modf_bug = modf((_X), &intpart_modf_bug);\
+ *(_Y) = intpart_modf_bug;\
+ result_modf_bug;\
+})
+#endif
+
+#if defined(__MINGW64__)
+/*
+ * Use powl() instead of broken pow() of x86_64-w64-mingw32.
+ * This workaround will fix test failures in test_bignum.rb,
+ * test_fixnum.rb and test_float.rb etc.
+ */
+static inline double
+rb_w32_pow(double x, double y)
+{
+ return (double)powl(x, y);
+}
+#elif defined(__MINGW64_VERSION_MAJOR)
+double rb_w32_pow(double x, double y);
+#endif
+#define pow rb_w32_pow
+#endif
+
+// mmap tiny emulation
+#define MAP_FAILED ((void *)-1)
+
+#define PROT_READ 0x01
+#define PROT_WRITE 0x02
+#define PROT_EXEC 0x04
+
+#define MAP_PRIVATE 0x0002
+#define MAP_ANON 0x1000
+#define MAP_ANONYMOUS MAP_ANON
+
+extern void *rb_w32_mmap(void *, size_t, int, int, int, rb_off_t);
+extern int rb_w32_munmap(void *, size_t);
+extern int rb_w32_mprotect(void *, size_t, int);
+
+#define mmap(a, l, p, f, d, o) rb_w32_mmap(a, l, p, f, d, o)
+#define munmap(a, l) rb_w32_munmap(a, l)
+#define mprotect(a, l, prot) rb_w32_mprotect(a, l, prot)
+
+#if defined(__cplusplus)
+#if 0
+{ /* satisfy cc-mode */
+#endif
+} /* extern "C" { */
+#endif
+
+#endif /* RUBY_WIN32_H */
lass='right'>13
-rw-r--r--spec/ruby/optional/capi/gc_spec.rb65
-rw-r--r--spec/ruby/optional/capi/globals_spec.rb89
-rw-r--r--spec/ruby/optional/capi/hash_spec.rb36
-rw-r--r--spec/ruby/optional/capi/integer_spec.rb15
-rw-r--r--spec/ruby/optional/capi/io_spec.rb105
-rw-r--r--spec/ruby/optional/capi/kernel_spec.rb349
-rw-r--r--spec/ruby/optional/capi/language_spec.rb37
-rw-r--r--spec/ruby/optional/capi/module_spec.rb78
-rw-r--r--spec/ruby/optional/capi/mutex_spec.rb15
-rw-r--r--spec/ruby/optional/capi/numeric_spec.rb302
-rw-r--r--spec/ruby/optional/capi/object_spec.rb358
-rw-r--r--spec/ruby/optional/capi/proc_spec.rb77
-rw-r--r--spec/ruby/optional/capi/range_spec.rb6
-rw-r--r--spec/ruby/optional/capi/rbasic_spec.rb42
-rw-r--r--spec/ruby/optional/capi/regexp_spec.rb59
-rw-r--r--spec/ruby/optional/capi/shared/rbasic.rb29
-rw-r--r--spec/ruby/optional/capi/spec_helper.rb59
-rw-r--r--spec/ruby/optional/capi/string_spec.rb618
-rw-r--r--spec/ruby/optional/capi/struct_spec.rb32
-rw-r--r--spec/ruby/optional/capi/symbol_spec.rb47
-rw-r--r--spec/ruby/optional/capi/thread_spec.rb84
-rw-r--r--spec/ruby/optional/capi/time_spec.rb104
-rw-r--r--spec/ruby/optional/capi/tracepoint_spec.rb56
-rw-r--r--spec/ruby/optional/capi/typed_data_spec.rb39
-rw-r--r--spec/ruby/optional/capi/util_spec.rb183
-rw-r--r--spec/ruby/security/cve_2010_1330_spec.rb6
-rw-r--r--spec/ruby/security/cve_2011_4815_spec.rb4
-rw-r--r--spec/ruby/security/cve_2013_4164_spec.rb4
-rw-r--r--spec/ruby/security/cve_2014_8080_spec.rb50
-rw-r--r--spec/ruby/security/cve_2017_17742_spec.rb57
-rw-r--r--spec/ruby/security/cve_2018_16396_spec.rb7
-rw-r--r--spec/ruby/security/cve_2018_6914_spec.rb61
-rw-r--r--spec/ruby/security/cve_2018_8778_spec.rb4
-rw-r--r--spec/ruby/security/cve_2018_8779_spec.rb4
-rw-r--r--spec/ruby/security/cve_2018_8780_spec.rb36
-rw-r--r--spec/ruby/security/cve_2019_8321_spec.rb20
-rw-r--r--spec/ruby/security/cve_2019_8322_spec.rb21
-rw-r--r--spec/ruby/security/cve_2019_8323_spec.rb36
-rw-r--r--spec/ruby/security/cve_2019_8325_spec.rb46
-rw-r--r--spec/ruby/security/cve_2020_10663_spec.rb39
-rw-r--r--spec/ruby/shared/basicobject/method_missing.rb18
-rw-r--r--spec/ruby/shared/basicobject/send.rb28
-rw-r--r--spec/ruby/shared/complex/Complex.rb141
-rw-r--r--spec/ruby/shared/complex/abs.rb12
-rw-r--r--spec/ruby/shared/complex/abs2.rb12
-rw-r--r--spec/ruby/shared/complex/coerce.rb70
-rw-r--r--spec/ruby/shared/complex/constants.rb7
-rw-r--r--spec/ruby/shared/complex/denominator.rb13
-rw-r--r--spec/ruby/shared/complex/divide.rb84
-rw-r--r--spec/ruby/shared/complex/equal_value.rb93
-rw-r--r--spec/ruby/shared/complex/exponent.rb61
-rw-r--r--spec/ruby/shared/complex/float/arg.rb38
-rw-r--r--spec/ruby/shared/complex/image.rb10
-rw-r--r--spec/ruby/shared/complex/inspect.rb14
-rw-r--r--spec/ruby/shared/complex/minus.rb45
-rw-r--r--spec/ruby/shared/complex/multiply.rb49
-rw-r--r--spec/ruby/shared/complex/numerator.rb19
-rw-r--r--spec/ruby/shared/complex/numeric/imag.rb26
-rw-r--r--spec/ruby/shared/complex/numeric/polar.rb50
-rw-r--r--spec/ruby/shared/complex/numeric/real.rb30
-rw-r--r--spec/ruby/shared/complex/plus.rb45
-rw-r--r--spec/ruby/shared/complex/polar.rb22
-rw-r--r--spec/ruby/shared/complex/real.rb8
-rw-r--r--spec/ruby/shared/complex/rect.rb96
-rw-r--r--spec/ruby/shared/complex/to_s.rb44
-rw-r--r--spec/ruby/shared/enumerable/minmax.rb24
-rw-r--r--spec/ruby/shared/enumerator/each.rb89
-rw-r--r--spec/ruby/shared/enumerator/enum_cons.rb12
-rw-r--r--spec/ruby/shared/enumerator/enum_for.rb7
-rw-r--r--spec/ruby/shared/enumerator/new.rb42
-rw-r--r--spec/ruby/shared/enumerator/next.rb28
-rw-r--r--spec/ruby/shared/enumerator/rewind.rb39
-rw-r--r--spec/ruby/shared/enumerator/with_index.rb11
-rw-r--r--spec/ruby/shared/fiber/resume.rb29
-rw-r--r--spec/ruby/shared/file/directory.rb6
-rw-r--r--spec/ruby/shared/file/executable.rb45
-rw-r--r--spec/ruby/shared/file/executable_real.rb43
-rw-r--r--spec/ruby/shared/file/exist.rb6
-rw-r--r--spec/ruby/shared/file/file.rb8
-rw-r--r--spec/ruby/shared/file/grpowned.rb3
-rw-r--r--spec/ruby/shared/file/identical.rb12
-rw-r--r--spec/ruby/shared/file/readable.rb21
-rw-r--r--spec/ruby/shared/file/readable_real.rb16
-rw-r--r--spec/ruby/shared/file/size.rb2
-rw-r--r--spec/ruby/shared/file/world_readable.rb14
-rw-r--r--spec/ruby/shared/file/world_writable.rb14
-rw-r--r--spec/ruby/shared/file/writable.rb18
-rw-r--r--spec/ruby/shared/file/writable_real.rb24
-rw-r--r--spec/ruby/shared/file/zero.rb8
-rw-r--r--spec/ruby/shared/hash/key_error.rb28
-rw-r--r--spec/ruby/shared/io/putc.rb12
-rw-r--r--spec/ruby/shared/kernel/complex.rb133
-rw-r--r--spec/ruby/shared/kernel/raise.rb123
-rw-r--r--spec/ruby/shared/math/atanh.rb8
-rw-r--r--spec/ruby/shared/process/abort.rb12
-rw-r--r--spec/ruby/shared/process/exit.rb52
-rw-r--r--spec/ruby/shared/process/fork.rb6
-rw-r--r--spec/ruby/shared/queue/clear.rb12
-rw-r--r--spec/ruby/shared/queue/close.rb14
-rw-r--r--spec/ruby/shared/queue/closed.rb (renamed from spec/ruby/library/thread/shared/queue/closed.rb)0
-rw-r--r--spec/ruby/shared/queue/deque.rb147
-rw-r--r--spec/ruby/shared/queue/empty.rb (renamed from spec/ruby/library/thread/shared/queue/empty.rb)0
-rw-r--r--spec/ruby/shared/queue/enque.rb18
-rw-r--r--spec/ruby/shared/queue/length.rb (renamed from spec/ruby/library/thread/shared/queue/length.rb)0
-rw-r--r--spec/ruby/shared/queue/num_waiting.rb (renamed from spec/ruby/library/thread/shared/queue/num_waiting.rb)0
-rw-r--r--spec/ruby/shared/rational/Rational.rb78
-rw-r--r--spec/ruby/shared/rational/arithmetic_exception_in_coerce.rb32
-rw-r--r--spec/ruby/shared/rational/coerce.rb13
-rw-r--r--spec/ruby/shared/rational/comparison.rb30
-rw-r--r--spec/ruby/shared/rational/div.rb12
-rw-r--r--spec/ruby/shared/rational/divide.rb4
-rw-r--r--spec/ruby/shared/rational/divmod.rb12
-rw-r--r--spec/ruby/shared/rational/exponent.rb58
-rw-r--r--spec/ruby/shared/rational/inspect.rb12
-rw-r--r--spec/ruby/shared/rational/minus.rb48
-rw-r--r--spec/ruby/shared/rational/modulo.rb8
-rw-r--r--spec/ruby/shared/rational/round.rb56
-rw-r--r--spec/ruby/shared/rational/to_f.rb6
-rw-r--r--spec/ruby/shared/rational/to_r.rb12
-rw-r--r--spec/ruby/shared/rational/to_s.rb7
-rw-r--r--spec/ruby/shared/sizedqueue/enque.rb113
-rw-r--r--spec/ruby/shared/sizedqueue/max.rb47
-rw-r--r--spec/ruby/shared/sizedqueue/new.rb23
-rw-r--r--spec/ruby/shared/sizedqueue/num_waiting.rb12
-rw-r--r--spec/ruby/shared/string/end_with.rb61
-rw-r--r--spec/ruby/shared/string/start_with.rb76
-rw-r--r--spec/ruby/shared/string/times.rb46
-rw-r--r--spec/ruby/shared/time/strftime_for_date.rb10
-rw-r--r--spec/ruby/shared/time/strftime_for_time.rb8
-rw-r--r--spec/ruby/spec_helper.rb10
-rw-r--r--spec/syntax_suggest/fixtures/derailed_require_tree.rb.txt74
-rwxr-xr-xspec/syntax_suggest/fixtures/rexe.rb.txt569
-rw-r--r--spec/syntax_suggest/fixtures/routes.rb.txt121
-rw-r--r--spec/syntax_suggest/fixtures/ruby_buildpack.rb.txt1344
-rw-r--r--spec/syntax_suggest/fixtures/syntax_tree.rb.txt9234
-rw-r--r--spec/syntax_suggest/fixtures/this_project_extra_def.rb.txt64
-rw-r--r--spec/syntax_suggest/fixtures/webmock.rb.txt35
-rw-r--r--spec/syntax_suggest/integration/exe_cli_spec.rb27
-rw-r--r--spec/syntax_suggest/integration/ruby_command_line_spec.rb193
-rw-r--r--spec/syntax_suggest/integration/syntax_suggest_spec.rb239
-rw-r--r--spec/syntax_suggest/spec_helper.rb104
-rw-r--r--spec/syntax_suggest/unit/api_spec.rb108
-rw-r--r--spec/syntax_suggest/unit/around_block_scan_spec.rb165
-rw-r--r--spec/syntax_suggest/unit/block_expand_spec.rb230
-rw-r--r--spec/syntax_suggest/unit/capture/before_after_keyword_ends_spec.rb47
-rw-r--r--spec/syntax_suggest/unit/capture/falling_indent_lines_spec.rb44
-rw-r--r--spec/syntax_suggest/unit/capture_code_context_spec.rb229
-rw-r--r--spec/syntax_suggest/unit/clean_document_spec.rb260
-rw-r--r--spec/syntax_suggest/unit/cli_spec.rb224
-rw-r--r--spec/syntax_suggest/unit/code_block_spec.rb77
-rw-r--r--spec/syntax_suggest/unit/code_frontier_spec.rb135
-rw-r--r--spec/syntax_suggest/unit/code_line_spec.rb165
-rw-r--r--spec/syntax_suggest/unit/code_search_spec.rb505
-rw-r--r--spec/syntax_suggest/unit/core_ext_spec.rb34
-rw-r--r--spec/syntax_suggest/unit/display_invalid_blocks_spec.rb174
-rw-r--r--spec/syntax_suggest/unit/explain_syntax_spec.rb255
-rw-r--r--spec/syntax_suggest/unit/lex_all_spec.rb29
-rw-r--r--spec/syntax_suggest/unit/pathname_from_message_spec.rb56
-rw-r--r--spec/syntax_suggest/unit/priority_queue_spec.rb95
-rw-r--r--spec/syntax_suggest/unit/scan_history_spec.rb114
-rw-r--r--sprintf.c1671
-rw-r--r--st.c1149
-rw-r--r--strftime.c70
-rw-r--r--string.c10839
-rw-r--r--string.rb552
-rw-r--r--struct.c1928
-rw-r--r--symbol.c1066
-rw-r--r--symbol.h41
-rw-r--r--symbol.rb15
-rw-r--r--template/Doxyfile.tmpl2506
-rw-r--r--template/GNUmakefile.in28
-rw-r--r--template/Makefile.in728
-rw-r--r--template/builtin_binary.inc.tmpl30
-rw-r--r--template/call_iseq_optimized.inc.tmpl68
-rw-r--r--template/configure-ext.mk.tmpl6
-rw-r--r--template/depend.tmpl2
-rw-r--r--template/encdb.h.tmpl21
-rw-r--r--template/extinit.c.tmpl2
-rw-r--r--template/exts.mk.tmpl41
-rw-r--r--template/fake.rb.in41
-rw-r--r--template/id.c.tmpl7
-rw-r--r--template/id.h.tmpl10
-rw-r--r--template/prelude.c.tmpl213
-rwxr-xr-xtemplate/ruby-gdb.in6
-rwxr-xr-xtemplate/ruby-lldb.in6
-rw-r--r--template/ruby-runner.h.in2
-rw-r--r--template/ruby.pc.in16
-rw-r--r--template/sizes.c.tmpl2
-rw-r--r--template/transdb.h.tmpl4
-rw-r--r--template/unicode_norm_gen.tmpl20
-rw-r--r--test/-ext-/arith_seq/test_arith_seq_beg_len_step.rb52
-rw-r--r--test/-ext-/arith_seq/test_arith_seq_extract.rb40
-rw-r--r--test/-ext-/array/test_resize.rb6
-rw-r--r--test/-ext-/array/test_to_ary_concat.rb20
-rw-r--r--test/-ext-/bignum/test_big2str.rb38
-rw-r--r--test/-ext-/bignum/test_bigzero.rb20
-rw-r--r--test/-ext-/bignum/test_div.rb38
-rw-r--r--test/-ext-/bignum/test_mul.rb260
-rw-r--r--test/-ext-/bignum/test_pack.rb737
-rw-r--r--test/-ext-/bignum/test_str2big.rb52
-rw-r--r--test/-ext-/bug_reporter/test_bug_reporter.rb15
-rw-r--r--test/-ext-/debug/test_debug.rb16
-rw-r--r--test/-ext-/debug/test_profile_frames.rb74
-rw-r--r--test/-ext-/econv/test_append.rb23
-rw-r--r--test/-ext-/eval/test_eval.rb12
-rw-r--r--test/-ext-/float/test_nextafter.rb4
-rw-r--r--test/-ext-/funcall/test_funcall.rb11
-rw-r--r--test/-ext-/funcall/test_passing_block.rb46
-rw-r--r--test/-ext-/gvl/test_last_thread.rb6
-rw-r--r--test/-ext-/gvl/test_ubf_async_safe.rb20
-rw-r--r--test/-ext-/integer/test_integer.rb11
-rw-r--r--test/-ext-/iseq_load/test_iseq_load.rb55
-rw-r--r--test/-ext-/iter/test_yield_block.rb2
-rw-r--r--test/-ext-/marshal/test_internal_ivar.rb8
-rw-r--r--test/-ext-/num2int/test_num2int.rb4
-rw-r--r--test/-ext-/popen_deadlock/test_popen_deadlock.rb2
-rw-r--r--test/-ext-/postponed_job/test_postponed_job.rb11
-rw-r--r--test/-ext-/rational/test_rat.rb44
-rw-r--r--test/-ext-/string/test_capacity.rb55
-rw-r--r--test/-ext-/string/test_cstr.rb14
-rw-r--r--test/-ext-/string/test_ellipsize.rb2
-rw-r--r--test/-ext-/string/test_enc_str_buf_cat.rb9
-rw-r--r--test/-ext-/string/test_fstring.rb40
-rw-r--r--test/-ext-/string/test_interned_str.rb12
-rw-r--r--test/-ext-/string/test_modify_expand.rb3
-rw-r--r--test/-ext-/string/test_normalize.rb8
-rw-r--r--test/-ext-/string/test_rb_str_dup.rb18
-rw-r--r--test/-ext-/string/test_set_len.rb39
-rw-r--r--test/-ext-/struct/test_duplicate.rb4
-rw-r--r--test/-ext-/symbol/noninterned_name.rb2
-rw-r--r--test/-ext-/symbol/test_inadvertent_creation.rb21
-rw-r--r--test/-ext-/symbol/test_type.rb14
-rw-r--r--test/-ext-/test_abi.rb47
-rw-r--r--test/-ext-/test_bug-14834.rb12
-rw-r--r--test/-ext-/test_enumerator_kw.rb11
-rw-r--r--test/-ext-/test_notimplement.rb7
-rw-r--r--test/-ext-/test_printf.rb13
-rw-r--r--test/-ext-/test_random.rb45
-rw-r--r--test/-ext-/test_scan_args.rb72
-rw-r--r--test/-ext-/thread/test_instrumentation_api.rb91
-rw-r--r--test/-ext-/thread_fd/test_thread_fd_close.rb24
-rw-r--r--test/-ext-/thread_fd_close/test_thread_fd_close.rb27
-rw-r--r--test/-ext-/tracepoint/test_tracepoint.rb24
-rw-r--r--test/-ext-/wait/test_wait.rb36
-rw-r--r--test/-ext-/wait_for_single_fd/test_wait_for_single_fd.rb49
-rw-r--r--test/-ext-/win32/test_console_attr.rb14
-rw-r--r--test/benchmark/test_benchmark.rb19
-rw-r--r--test/bigdecimal/helper.rb39
-rw-r--r--test/bigdecimal/test_bigdecimal.rb681
-rw-r--r--test/bigdecimal/test_bigdecimal_util.rb95
-rw-r--r--test/bigdecimal/test_bigmath.rb2
-rw-r--r--test/bigdecimal/test_ractor.rb23
-rw-r--r--test/bigdecimal/testbase.rb28
-rw-r--r--test/cgi/test_cgi_cookie.rb90
-rw-r--r--test/cgi/test_cgi_core.rb4
-rw-r--r--test/cgi/test_cgi_header.rb8
-rw-r--r--test/cgi/test_cgi_multipart.rb3
-rw-r--r--test/cgi/test_cgi_util.rb175
-rw-r--r--test/coverage/autostart.rb2
-rw-r--r--test/coverage/main.rb1
-rw-r--r--test/coverage/test_coverage.rb518
-rw-r--r--test/csv/base.rb9
-rw-r--r--test/csv/helper.rb42
-rw-r--r--test/csv/interface/test_delegation.rb47
-rw-r--r--test/csv/interface/test_read.rb381
-rw-r--r--test/csv/interface/test_read_write.rb124
-rw-r--r--test/csv/interface/test_write.rb217
-rw-r--r--test/csv/parse/test_column_separator.rb40
-rw-r--r--test/csv/parse/test_convert.rb165
-rw-r--r--test/csv/parse/test_each.rb23
-rw-r--r--test/csv/parse/test_general.rb341
-rw-r--r--test/csv/parse/test_header.rb342
-rw-r--r--test/csv/parse/test_inputs_scanner.rb63
-rw-r--r--test/csv/parse/test_invalid.rb52
-rw-r--r--test/csv/parse/test_liberal_parsing.rb171
-rw-r--r--test/csv/parse/test_quote_char_nil.rb93
-rw-r--r--test/csv/parse/test_read.rb27
-rw-r--r--test/csv/parse/test_rewind.rb40
-rw-r--r--test/csv/parse/test_row_separator.rb16
-rw-r--r--test/csv/parse/test_skip_lines.rb118
-rw-r--r--test/csv/parse/test_strip.rb112
-rw-r--r--test/csv/parse/test_unconverted_fields.rb117
-rwxr-xr-xtest/csv/test_csv_parsing.rb252
-rwxr-xr-xtest/csv/test_csv_writing.rb95
-rw-r--r--[-rwxr-xr-x]test/csv/test_data_converters.rb266
-rw-r--r--[-rwxr-xr-x]test/csv/test_encodings.rb97
-rw-r--r--[-rwxr-xr-x]test/csv/test_features.rb165
-rwxr-xr-xtest/csv/test_headers.rb318
-rwxr-xr-xtest/csv/test_interface.rb443
-rw-r--r--test/csv/test_patterns.rb27
-rw-r--r--[-rwxr-xr-x]test/csv/test_row.rb33
-rw-r--r--[-rwxr-xr-x]test/csv/test_table.rb114
-rw-r--r--test/csv/ts_all.rb19
-rw-r--r--test/csv/write/test_converters.rb53
-rw-r--r--test/csv/write/test_force_quotes.rb78
-rw-r--r--test/csv/write/test_general.rb246
-rw-r--r--test/csv/write/test_quote_empty.rb70
-rw-r--r--test/date/test_date.rb54
-rw-r--r--test/date/test_date_arith.rb6
-rw-r--r--test/date/test_date_attr.rb20
-rw-r--r--test/date/test_date_conv.rb35
-rw-r--r--test/date/test_date_marshal.rb15
-rw-r--r--test/date/test_date_new.rb110
-rw-r--r--test/date/test_date_parse.rb209
-rw-r--r--test/date/test_date_ractor.rb27
-rw-r--r--test/date/test_date_strftime.rb20
-rw-r--r--test/date/test_date_strptime.rb28
-rw-r--r--test/date/test_switch_hitter.rb57
-rw-r--r--test/dbm/test_dbm.rb635
-rw-r--r--test/did_you_mean/core_ext/test_name_error_extension.rb55
-rw-r--r--test/did_you_mean/edit_distance/test_jaro_winkler.rb36
-rw-r--r--test/did_you_mean/fixtures/book.rb4
-rw-r--r--test/did_you_mean/fixtures/mini_dir.yml15
-rw-r--r--test/did_you_mean/fixtures/rspec_dir.yml112
-rw-r--r--test/did_you_mean/helper.rb43
-rw-r--r--test/did_you_mean/spell_checking/test_class_name_check.rb81
-rw-r--r--test/did_you_mean/spell_checking/test_key_name_check.rb54
-rw-r--r--test/did_you_mean/spell_checking/test_method_name_check.rb165
-rw-r--r--test/did_you_mean/spell_checking/test_pattern_key_name_check.rb20
-rw-r--r--test/did_you_mean/spell_checking/test_require_path_check.rb32
-rw-r--r--test/did_you_mean/spell_checking/test_uncorrectable_name_check.rb15
-rw-r--r--test/did_you_mean/spell_checking/test_variable_name_check.rb152
-rw-r--r--test/did_you_mean/test_ractor_compatibility.rb117
-rw-r--r--test/did_you_mean/test_spell_checker.rb78
-rw-r--r--test/did_you_mean/test_tree_spell_checker.rb170
-rw-r--r--test/did_you_mean/tree_spell/change_word.rb61
-rw-r--r--test/did_you_mean/tree_spell/human_typo.rb69
-rw-r--r--test/did_you_mean/tree_spell/test_change_word.rb38
-rw-r--r--test/did_you_mean/tree_spell/test_human_typo.rb24
-rw-r--r--test/digest/test_digest.rb8
-rw-r--r--test/digest/test_digest_extend.rb15
-rw-r--r--test/digest/test_ractor.rb97
-rw-r--r--test/drb/drbtest.rb108
-rw-r--r--test/drb/test_drb.rb136
-rw-r--r--test/drb/test_drbobject.rb69
-rw-r--r--test/drb/test_drbssl.rb63
-rw-r--r--test/drb/test_drbunix.rb20
-rw-r--r--test/drb/ut_drb.rb21
-rw-r--r--test/dtrace/helper.rb25
-rw-r--r--test/dtrace/test_array_create.rb6
-rw-r--r--test/dtrace/test_function_entry.rb6
-rw-r--r--test/dtrace/test_hash_create.rb6
-rw-r--r--test/dtrace/test_require.rb6
-rw-r--r--test/dtrace/test_singleton_function.rb6
-rw-r--r--test/dtrace/test_string.rb6
-rw-r--r--test/erb/test_erb.rb80
-rw-r--r--test/erb/test_erb_command.rb22
-rw-r--r--test/error_highlight/test_error_highlight.rb1290
-rw-r--r--test/etc/test_etc.rb28
-rw-r--r--test/excludes/TestArray.rb1
-rw-r--r--test/excludes/TestGem.rb4
-rw-r--r--test/excludes/TestThread.rb12
-rw-r--r--test/excludes/TestThreadQueue.rb9
-rw-r--r--test/excludes/_appveyor/TestArray.rb7
-rw-r--r--test/fiber/autoload.rb3
-rw-r--r--test/fiber/http.rb54
-rw-r--r--test/fiber/scheduler.rb363
-rw-r--r--test/fiber/test_address_resolve.rb278
-rw-r--r--test/fiber/test_backtrace.rb22
-rw-r--r--test/fiber/test_enumerator.rb53
-rw-r--r--test/fiber/test_io.rb222
-rw-r--r--test/fiber/test_io_buffer.rb158
-rw-r--r--test/fiber/test_mutex.rb240
-rw-r--r--test/fiber/test_process.rb72
-rw-r--r--test/fiber/test_queue.rb54
-rw-r--r--test/fiber/test_ractor.rb23
-rw-r--r--test/fiber/test_scheduler.rb226
-rw-r--r--test/fiber/test_sleep.rb71
-rw-r--r--test/fiber/test_storage.rb115
-rw-r--r--test/fiber/test_thread.rb130
-rw-r--r--test/fiber/test_timeout.rb51
-rw-r--r--test/fiddle/helper.rb83
-rw-r--r--test/fiddle/test_c_struct_builder.rb69
-rw-r--r--test/fiddle/test_c_struct_entry.rb130
-rw-r--r--test/fiddle/test_c_union_entity.rb15
-rw-r--r--test/fiddle/test_closure.rb106
-rw-r--r--test/fiddle/test_cparser.rb169
-rw-r--r--test/fiddle/test_fiddle.rb41
-rw-r--r--test/fiddle/test_func.rb120
-rw-r--r--test/fiddle/test_function.rb148
-rw-r--r--test/fiddle/test_handle.rb65
-rw-r--r--test/fiddle/test_import.rb395
-rw-r--r--test/fiddle/test_memory_view.rb143
-rw-r--r--test/fiddle/test_pack.rb37
-rw-r--r--test/fiddle/test_pinned.rb28
-rw-r--r--test/fiddle/test_pointer.rb120
-rw-r--r--test/fileutils/clobber.rb5
-rw-r--r--test/fileutils/test_dryrun.rb2
-rw-r--r--test/fileutils/test_fileutils.rb236
-rw-r--r--test/fileutils/test_nowrite.rb2
-rw-r--r--test/fileutils/test_verbose.rb2
-rw-r--r--test/fileutils/visibility_tests.rb5
-rw-r--r--test/fixtures/fake_sorted_set_gem/sorted_set.rb9
-rw-r--r--test/gdbm/test_gdbm.rb734
-rw-r--r--test/io/console/test_io_console.rb196
-rw-r--r--test/io/nonblock/test_flush.rb3
-rw-r--r--test/io/wait/test_io_wait.rb58
-rw-r--r--test/io/wait/test_io_wait_uncommon.rb39
-rw-r--r--test/io/wait/test_ractor.rb17
-rw-r--r--test/irb/helper.rb76
-rw-r--r--test/irb/test_cmd.rb692
-rw-r--r--test/irb/test_color.rb291
-rw-r--r--test/irb/test_color_printer.rb69
-rw-r--r--test/irb/test_completion.rb349
-rw-r--r--test/irb/test_context.rb621
-rw-r--r--test/irb/test_debug_cmd.rb303
-rw-r--r--test/irb/test_history.rb209
-rw-r--r--test/irb/test_init.rb150
-rw-r--r--test/irb/test_input_method.rb79
-rw-r--r--test/irb/test_option.rb7
-rw-r--r--test/irb/test_raise_no_backtrace_exception.rb48
-rw-r--r--test/irb/test_ruby-lex.rb108
-rw-r--r--test/irb/test_ruby_lex.rb725
-rw-r--r--test/irb/test_workspace.rb53
-rw-r--r--test/irb/yamatanooroti/test_rendering.rb249
-rw-r--r--test/json/fixtures/fail29.json1
-rw-r--r--test/json/fixtures/fail30.json1
-rw-r--r--test/json/fixtures/fail31.json1
-rw-r--r--test/json/fixtures/fail32.json1
-rw-r--r--test/json/json_addition_test.rb8
-rw-r--r--test/json/json_common_interface_test.rb51
-rw-r--r--test/json/json_fixtures_test.rb10
-rw-r--r--test/json/json_generator_test.rb38
-rw-r--r--test/json/json_parser_test.rb58
-rw-r--r--test/json/ractor_test.rb30
-rw-r--r--test/json/test_helper.rb10
-rw-r--r--test/lib/-test-/integer.rb14
-rw-r--r--test/lib/envutil.rb298
-rw-r--r--test/lib/iseq_loader_checker.rb75
-rw-r--r--test/lib/jit_support.rb89
-rw-r--r--test/lib/leakchecker.rb240
-rw-r--r--test/lib/memory_status.rb149
-rw-r--r--test/lib/minitest/README.txt457
-rw-r--r--test/lib/minitest/autorun.rb14
-rw-r--r--test/lib/minitest/benchmark.rb418
-rw-r--r--test/lib/minitest/mock.rb196
-rw-r--r--test/lib/minitest/unit.rb1424
-rw-r--r--test/lib/profile_test_all.rb91
-rw-r--r--test/lib/test/unit.rb1179
-rw-r--r--test/lib/test/unit/assertions.rb945
-rw-r--r--test/lib/test/unit/parallel.rb208
-rw-r--r--test/lib/test/unit/testcase.rb36
-rw-r--r--test/lib/tracepointchecker.rb126
-rw-r--r--test/lib/with_different_ofs.rb4
-rw-r--r--test/logger/test_formatter.rb35
-rw-r--r--test/logger/test_logdevice.rb35
-rw-r--r--test/logger/test_logger.rb57
-rw-r--r--test/logger/test_logperiod.rb80
-rw-r--r--test/logger/test_severity.rb12
-rw-r--r--test/matrix/test_matrix.rb651
-rw-r--r--test/matrix/test_vector.rb233
-rw-r--r--test/minitest/metametameta.rb71
-rw-r--r--test/minitest/test_minitest_benchmark.rb131
-rw-r--r--test/minitest/test_minitest_mock.rb404
-rw-r--r--test/minitest/test_minitest_unit.rb1779
-rw-r--r--test/misc/test_ruby_mode.rb183
-rw-r--r--test/mkmf/base.rb236
-rw-r--r--test/mkmf/test_config.rb16
-rw-r--r--test/mkmf/test_constant.rb60
-rw-r--r--test/mkmf/test_convertible.rb48
-rw-r--r--test/mkmf/test_egrep_cpp.rb13
-rw-r--r--test/mkmf/test_find_executable.rb82
-rw-r--r--test/mkmf/test_flags.rb92
-rw-r--r--test/mkmf/test_framework.rb70
-rw-r--r--test/mkmf/test_have_func.rb18
-rw-r--r--test/mkmf/test_have_library.rb84
-rw-r--r--test/mkmf/test_have_macro.rb46
-rw-r--r--test/mkmf/test_install.rb28
-rw-r--r--test/mkmf/test_libs.rb153
-rw-r--r--test/mkmf/test_mkmf.rb13
-rw-r--r--test/mkmf/test_pkg_config.rb66
-rw-r--r--test/mkmf/test_signedness.rb38
-rw-r--r--test/mkmf/test_sizeof.rb74
-rw-r--r--test/monitor/test_monitor.rb107
-rw-r--r--test/net/fixtures/Makefile15
-rw-r--r--test/net/fixtures/cacert.pem86
-rw-r--r--test/net/fixtures/server.crt65
-rw-r--r--test/net/fixtures/server.key38
-rw-r--r--test/net/ftp/test_buffered_socket.rb48
-rw-r--r--test/net/ftp/test_ftp.rb2471
-rw-r--r--test/net/ftp/test_mlsx_entry.rb98
-rw-r--r--test/net/http/test_http.rb268
-rw-r--r--test/net/http/test_http_request.rb7
-rw-r--r--test/net/http/test_httpheader.rb46
-rw-r--r--test/net/http/test_httpresponse.rb287
-rw-r--r--test/net/http/test_https.rb191
-rw-r--r--test/net/http/test_https_proxy.rb2
-rw-r--r--test/net/http/utils.rb25
-rw-r--r--test/net/imap/Makefile15
-rw-r--r--test/net/imap/test_imap.rb772
-rw-r--r--test/net/imap/test_imap_response_parser.rb324
-rw-r--r--test/net/pop/test_pop.rb166
-rw-r--r--test/net/protocol/test_protocol.rb53
-rw-r--r--test/net/smtp/test_response.rb100
-rw-r--r--test/net/smtp/test_smtp.rb200
-rw-r--r--test/net/smtp/test_ssl_socket.rb97
-rw-r--r--test/objspace/test_objspace.rb461
-rw-r--r--test/objspace/test_ractor.rb17
-rw-r--r--test/open-uri/test_open-uri.rb363
-rw-r--r--test/open-uri/test_ssl.rb372
-rw-r--r--test/openssl/fixtures/pkey/certificate.derbin0 -> 1325 bytes-rw-r--r--test/openssl/fixtures/pkey/dh-1.pem13
-rw-r--r--test/openssl/fixtures/pkey/empty.der0
-rw-r--r--test/openssl/fixtures/pkey/empty.pem0
-rw-r--r--test/openssl/fixtures/pkey/fullchain.pem56
-rw-r--r--test/openssl/fixtures/pkey/garbage.txt1
-rw-r--r--test/openssl/fixtures/pkey/p256_too_large.pem5
-rw-r--r--test/openssl/fixtures/pkey/p384_invalid.pem6
-rw-r--r--test/openssl/fixtures/pkey/rsa-1.pem51
-rw-r--r--test/openssl/fixtures/pkey/rsa-2.pem51
-rw-r--r--test/openssl/fixtures/pkey/rsa-3.pem51
-rw-r--r--test/openssl/test_asn1.rb66
-rw-r--r--test/openssl/test_bn.rb131
-rw-r--r--test/openssl/test_buffering.rb11
-rw-r--r--test/openssl/test_cipher.rb90
-rw-r--r--test/openssl/test_config.rb194
-rw-r--r--test/openssl/test_digest.rb64
-rw-r--r--test/openssl/test_engine.rb4
-rw-r--r--test/openssl/test_fips.rb2
-rw-r--r--test/openssl/test_hmac.rb33
-rw-r--r--test/openssl/test_kdf.rb2
-rw-r--r--test/openssl/test_ns_spki.rb6
-rw-r--r--test/openssl/test_ocsp.rb46
-rw-r--r--test/openssl/test_ossl.rb65
-rw-r--r--test/openssl/test_pair.rb86
-rw-r--r--test/openssl/test_pkcs12.rb299
-rw-r--r--test/openssl/test_pkcs7.rb42
-rw-r--r--test/openssl/test_pkey.rb177
-rw-r--r--test/openssl/test_pkey_dh.rb111
-rw-r--r--test/openssl/test_pkey_dsa.rb109
-rw-r--r--test/openssl/test_pkey_ec.rb231
-rw-r--r--test/openssl/test_pkey_rsa.rb383
-rw-r--r--test/openssl/test_random.rb2
-rw-r--r--test/openssl/test_ssl.rb864
-rw-r--r--test/openssl/test_ssl_session.rb13
-rw-r--r--test/openssl/test_ts.rb680
-rw-r--r--test/openssl/test_x509attr.rb12
-rw-r--r--test/openssl/test_x509cert.rb143
-rw-r--r--test/openssl/test_x509crl.rb45
-rw-r--r--test/openssl/test_x509ext.rb15
-rw-r--r--test/openssl/test_x509name.rb35
-rw-r--r--test/openssl/test_x509req.rb45
-rw-r--r--test/openssl/test_x509store.rb414
-rw-r--r--test/openssl/ut_eof.rb14
-rw-r--r--test/openssl/utils.rb149
-rw-r--r--test/optparse/test_acceptable.rb3
-rw-r--r--test/optparse/test_autoconf.rb11
-rw-r--r--test/optparse/test_bash_completion.rb4
-rw-r--r--test/optparse/test_cclass.rb2
-rw-r--r--test/optparse/test_did_you_mean.rb48
-rw-r--r--test/optparse/test_getopts.rb4
-rw-r--r--test/optparse/test_kwargs.rb4
-rw-r--r--test/optparse/test_load.rb141
-rw-r--r--test/optparse/test_noarg.rb6
-rw-r--r--test/optparse/test_optarg.rb2
-rw-r--r--test/optparse/test_optparse.rb45
-rw-r--r--test/optparse/test_placearg.rb8
-rw-r--r--test/optparse/test_reqarg.rb10
-rw-r--r--test/optparse/test_summary.rb25
-rw-r--r--test/optparse/test_zsh_completion.rb4
-rw-r--r--test/ostruct/test_ostruct.rb207
-rw-r--r--test/pathname/test_pathname.rb296
-rw-r--r--test/pathname/test_ractor.rb22
-rw-r--r--test/psych/helper.rb40
-rw-r--r--test/psych/test_alias_and_anchor.rb12
-rw-r--r--test/psych/test_array.rb22
-rw-r--r--test/psych/test_class.rb4
-rw-r--r--test/psych/test_coder.rb141
-rw-r--r--test/psych/test_date_time.rb24
-rw-r--r--test/psych/test_deprecated.rb4
-rw-r--r--test/psych/test_document.rb2
-rw-r--r--test/psych/test_emitter.rb10
-rw-r--r--test/psych/test_encoding.rb11
-rw-r--r--test/psych/test_exception.rb79
-rw-r--r--test/psych/test_hash.rb100
-rw-r--r--test/psych/test_marshalable.rb12
-rw-r--r--test/psych/test_merge_keys.rb34
-rw-r--r--test/psych/test_nil.rb4
-rw-r--r--test/psych/test_numeric.rb12
-rw-r--r--test/psych/test_object.rb15
-rw-r--r--test/psych/test_object_references.rb12
-rw-r--r--test/psych/test_omap.rb4
-rw-r--r--test/psych/test_parser.rb24
-rw-r--r--test/psych/test_psych.rb187
-rw-r--r--test/psych/test_ractor.rb50
-rw-r--r--test/psych/test_safe_load.rb137
-rw-r--r--test/psych/test_scalar_scanner.rb92
-rw-r--r--test/psych/test_serialize_subclasses.rb4
-rw-r--r--test/psych/test_set.rb6
-rw-r--r--test/psych/test_string.rb27
-rw-r--r--test/psych/test_struct.rb6
-rw-r--r--test/psych/test_tainted.rb131
-rw-r--r--test/psych/test_yaml.rb41
-rw-r--r--test/psych/test_yaml_special_cases.rb24
-rw-r--r--test/psych/test_yamlstore.rb47
-rw-r--r--test/psych/visitors/test_to_ruby.rb5
-rw-r--r--test/psych/visitors/test_yaml_tree.rb15
-rw-r--r--test/racc/assets/cadenza.y170
-rw-r--r--test/racc/assets/cast.y926
-rw-r--r--test/racc/assets/chk.y126
-rw-r--r--test/racc/assets/conf.y16
-rw-r--r--test/racc/assets/csspool.y729
-rw-r--r--test/racc/assets/digraph.y29
-rw-r--r--test/racc/assets/echk.y118
-rw-r--r--test/racc/assets/edtf.y583
-rw-r--r--test/racc/assets/err.y60
-rw-r--r--test/racc/assets/error_recovery.y35
-rw-r--r--test/racc/assets/expect.y7
-rw-r--r--test/racc/assets/firstline.y4
-rw-r--r--test/racc/assets/huia.y318
-rw-r--r--test/racc/assets/ichk.y102
-rw-r--r--test/racc/assets/ifelse.y14
-rw-r--r--test/racc/assets/intp.y546
-rw-r--r--test/racc/assets/journey.y47
-rw-r--r--test/racc/assets/liquor.y313
-rw-r--r--test/racc/assets/machete.y423
-rw-r--r--test/racc/assets/macruby.y2197
-rw-r--r--test/racc/assets/mailp.y437
-rw-r--r--test/racc/assets/mediacloth.y599
-rw-r--r--test/racc/assets/mof.y649
-rw-r--r--test/racc/assets/namae.y302
-rw-r--r--test/racc/assets/nasl.y626
-rw-r--r--test/racc/assets/newsyn.y25
-rw-r--r--test/racc/assets/noend.y4
-rw-r--r--test/racc/assets/nokogiri-css.y255
-rw-r--r--test/racc/assets/nonass.y41
-rw-r--r--test/racc/assets/normal.y27
-rw-r--r--test/racc/assets/norule.y4
-rw-r--r--test/racc/assets/nullbug1.y25
-rw-r--r--test/racc/assets/nullbug2.y15
-rw-r--r--test/racc/assets/opal.y1807
-rw-r--r--test/racc/assets/opt.y123
-rw-r--r--test/racc/assets/percent.y35
-rw-r--r--test/racc/assets/php_serialization.y98
-rw-r--r--test/racc/assets/recv.y97
-rw-r--r--test/racc/assets/riml.y665
-rw-r--r--test/racc/assets/rrconf.y14
-rw-r--r--test/racc/assets/ruby18.y1943
-rw-r--r--test/racc/assets/ruby19.y2174
-rw-r--r--test/racc/assets/ruby20.y2350
-rw-r--r--test/racc/assets/ruby21.y2359
-rw-r--r--test/racc/assets/ruby22.y2381
-rw-r--r--test/racc/assets/scan.y72
-rw-r--r--test/racc/assets/syntax.y50
-rw-r--r--test/racc/assets/tp_plus.y622
-rw-r--r--test/racc/assets/twowaysql.y278
-rw-r--r--test/racc/assets/unterm.y5
-rw-r--r--test/racc/assets/useless.y12
-rw-r--r--test/racc/assets/yyerr.y46
-rw-r--r--test/racc/bench.y36
-rw-r--r--test/racc/case.rb111
-rw-r--r--test/racc/infini.y8
-rw-r--r--test/racc/regress/README.txt7
-rw-r--r--test/racc/regress/cadenza796
-rw-r--r--test/racc/regress/cast3945
-rw-r--r--test/racc/regress/csspool2314
-rw-r--r--test/racc/regress/edtf1794
-rw-r--r--test/racc/regress/huia1681
-rw-r--r--test/racc/regress/journey222
-rw-r--r--test/racc/regress/liquor885
-rw-r--r--test/racc/regress/machete833
-rw-r--r--test/racc/regress/mediacloth1463
-rw-r--r--test/racc/regress/mof1368
-rw-r--r--test/racc/regress/namae634
-rw-r--r--test/racc/regress/nasl2548
-rw-r--r--test/racc/regress/nokogiri-css836
-rw-r--r--test/racc/regress/opal10107
-rw-r--r--test/racc/regress/php_serialization336
-rw-r--r--test/racc/regress/riml4037
-rw-r--r--test/racc/regress/ruby189945
-rw-r--r--test/racc/regress/ruby2211180
-rw-r--r--test/racc/regress/tp_plus1933
-rw-r--r--test/racc/regress/twowaysql556
-rw-r--r--test/racc/scandata/brace7
-rw-r--r--test/racc/scandata/gvar1
-rw-r--r--test/racc/scandata/normal4
-rw-r--r--test/racc/scandata/percent18
-rw-r--r--test/racc/scandata/slash10
-rw-r--r--test/racc/src.intp34
-rw-r--r--test/racc/start.y20
-rw-r--r--test/racc/test_chk_y.rb52
-rw-r--r--test/racc/test_grammar_file_parser.rb15
-rw-r--r--test/racc/test_racc_command.rb339
-rw-r--r--test/racc/test_scan_y.rb52
-rw-r--r--test/racc/testscanner.rb51
-rw-r--r--test/rdoc/MarkdownTest_1.0.3/Markdown Documentation - Basics.text4
-rw-r--r--test/rdoc/helper.rb5
-rw-r--r--test/rdoc/minitest_helper.rb203
-rw-r--r--test/rdoc/support/formatter_test_case.rb764
-rw-r--r--test/rdoc/support/test_case.rb217
-rw-r--r--test/rdoc/support/text_formatter_test_case.rb (renamed from lib/rdoc/markup/text_formatter_test_case.rb)0
-rw-r--r--test/rdoc/test_rdoc_alias.rb3
-rw-r--r--test/rdoc/test_rdoc_any_method.rb68
-rw-r--r--test/rdoc/test_rdoc_attr.rb2
-rw-r--r--test/rdoc/test_rdoc_class_module.rb54
-rw-r--r--test/rdoc/test_rdoc_code_object.rb2
-rw-r--r--test/rdoc/test_rdoc_comment.rb5
-rw-r--r--test/rdoc/test_rdoc_constant.rb2
-rw-r--r--test/rdoc/test_rdoc_context.rb8
-rw-r--r--test/rdoc/test_rdoc_context_section.rb12
-rw-r--r--test/rdoc/test_rdoc_cross_reference.rb61
-rw-r--r--test/rdoc/test_rdoc_encoding.rb8
-rw-r--r--test/rdoc/test_rdoc_extend.rb11
-rw-r--r--test/rdoc/test_rdoc_generator_darkfish.rb118
-rw-r--r--test/rdoc/test_rdoc_generator_json_index.rb28
-rw-r--r--test/rdoc/test_rdoc_generator_markup.rb2
-rw-r--r--test/rdoc/test_rdoc_generator_pot.rb2
-rw-r--r--test/rdoc/test_rdoc_generator_pot_po.rb2
-rw-r--r--test/rdoc/test_rdoc_generator_pot_po_entry.rb2
-rw-r--r--test/rdoc/test_rdoc_generator_ri.rb2
-rw-r--r--test/rdoc/test_rdoc_i18n_locale.rb10
-rw-r--r--test/rdoc/test_rdoc_i18n_text.rb2
-rw-r--r--test/rdoc/test_rdoc_include.rb14
-rw-r--r--test/rdoc/test_rdoc_markdown.rb92
-rw-r--r--test/rdoc/test_rdoc_markdown_test.rb10
-rw-r--r--test/rdoc/test_rdoc_markup.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_attribute_manager.rb57
-rw-r--r--test/rdoc/test_rdoc_markup_attributes.rb10
-rw-r--r--test/rdoc/test_rdoc_markup_document.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_formatter.rb40
-rw-r--r--test/rdoc/test_rdoc_markup_hard_break.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_heading.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_include.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_indented_paragraph.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_paragraph.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_parser.rb60
-rw-r--r--test/rdoc/test_rdoc_markup_pre_process.rb4
-rw-r--r--test/rdoc/test_rdoc_markup_raw.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_to_ansi.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_to_bs.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_to_html.rb169
-rw-r--r--test/rdoc/test_rdoc_markup_to_html_crossref.rb125
-rw-r--r--test/rdoc/test_rdoc_markup_to_html_snippet.rb14
-rw-r--r--test/rdoc/test_rdoc_markup_to_joined_paragraph.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_to_label.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_to_markdown.rb6
-rw-r--r--test/rdoc/test_rdoc_markup_to_rdoc.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_to_table_of_contents.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_to_tt_only.rb2
-rw-r--r--test/rdoc/test_rdoc_markup_verbatim.rb2
-rw-r--r--test/rdoc/test_rdoc_method_attr.rb3
-rw-r--r--test/rdoc/test_rdoc_normal_class.rb7
-rw-r--r--test/rdoc/test_rdoc_normal_module.rb3
-rw-r--r--test/rdoc/test_rdoc_options.rb241
-rw-r--r--test/rdoc/test_rdoc_parser.rb6
-rw-r--r--test/rdoc/test_rdoc_parser_c.rb327
-rw-r--r--test/rdoc/test_rdoc_parser_changelog.rb171
-rw-r--r--test/rdoc/test_rdoc_parser_markdown.rb2
-rw-r--r--test/rdoc/test_rdoc_parser_rd.rb2
-rw-r--r--test/rdoc/test_rdoc_parser_ruby.rb225
-rw-r--r--test/rdoc/test_rdoc_parser_simple.rb2
-rw-r--r--test/rdoc/test_rdoc_rd.rb2
-rw-r--r--test/rdoc/test_rdoc_rd_block_parser.rb23
-rw-r--r--test/rdoc/test_rdoc_rd_inline.rb2
-rw-r--r--test/rdoc/test_rdoc_rd_inline_parser.rb2
-rw-r--r--test/rdoc/test_rdoc_rdoc.rb145
-rw-r--r--test/rdoc/test_rdoc_require.rb3
-rw-r--r--test/rdoc/test_rdoc_ri_driver.rb184
-rw-r--r--test/rdoc/test_rdoc_ri_paths.rb14
-rw-r--r--test/rdoc/test_rdoc_rubygems_hook.rb93
-rw-r--r--test/rdoc/test_rdoc_servlet.rb41
-rw-r--r--test/rdoc/test_rdoc_single_class.rb2
-rw-r--r--test/rdoc/test_rdoc_stats.rb2
-rw-r--r--test/rdoc/test_rdoc_store.rb35
-rw-r--r--test/rdoc/test_rdoc_task.rb17
-rw-r--r--test/rdoc/test_rdoc_text.rb26
-rw-r--r--test/rdoc/test_rdoc_token_stream.rb2
-rw-r--r--test/rdoc/test_rdoc_tom_doc.rb2
-rw-r--r--test/rdoc/test_rdoc_top_level.rb10
-rw-r--r--test/rdoc/xref_data.rb17
-rw-r--r--test/rdoc/xref_test_case.rb15
-rw-r--r--test/readline/helper.rb29
-rw-r--r--test/readline/test_readline.rb628
-rw-r--r--test/readline/test_readline_history.rb197
-rw-r--r--test/reline/helper.rb125
-rw-r--r--test/reline/test_config.rb412
-rw-r--r--test/reline/test_history.rb308
-rw-r--r--test/reline/test_key_actor_emacs.rb2354
-rw-r--r--test/reline/test_key_actor_vi.rb1465
-rw-r--r--test/reline/test_key_stroke.rb79
-rw-r--r--test/reline/test_kill_ring.rb268
-rw-r--r--test/reline/test_macro.rb41
-rw-r--r--test/reline/test_reline.rb386
-rw-r--r--test/reline/test_reline_key.rb53
-rw-r--r--test/reline/test_string_processing.rb81
-rw-r--r--test/reline/test_terminfo.rb61
-rw-r--r--test/reline/test_unicode.rb25
-rw-r--r--test/reline/test_within_pipe.rb78
-rw-r--r--test/reline/windows/test_key_event_record.rb41
-rwxr-xr-xtest/reline/yamatanooroti/multiline_repl215
-rw-r--r--test/reline/yamatanooroti/termination_checker.rb30
-rw-r--r--test/reline/yamatanooroti/test_rendering.rb1345
-rw-r--r--test/resolv/test_addr.rb26
-rw-r--r--test/resolv/test_dns.rb213
-rw-r--r--test/rexml/data/LostineRiver.kml.gzbin50154 -> 0 bytes-rw-r--r--test/rexml/data/ProductionSupport.xml29
-rw-r--r--test/rexml/data/axis.xml25
-rw-r--r--test/rexml/data/bad.xml5
-rw-r--r--test/rexml/data/basic.xml11
-rw-r--r--test/rexml/data/basicupdate.xml47
-rw-r--r--test/rexml/data/broken.rss20
-rw-r--r--test/rexml/data/contents.xml70
-rw-r--r--test/rexml/data/dash.xml12
-rw-r--r--test/rexml/data/defaultNamespace.xml6
-rw-r--r--test/rexml/data/doctype_test.xml34
-rw-r--r--test/rexml/data/documentation.xml542
-rw-r--r--test/rexml/data/euc.xml296
-rw-r--r--test/rexml/data/evaluate.xml28
-rw-r--r--test/rexml/data/fibo.xml29
-rw-r--r--test/rexml/data/foo.xml10
-rw-r--r--test/rexml/data/google.2.xml156
-rw-r--r--test/rexml/data/id.xml21
-rw-r--r--test/rexml/data/iso8859-1.xml4
-rw-r--r--test/rexml/data/jaxen24.xml2
-rw-r--r--test/rexml/data/jaxen3.xml15
-rw-r--r--test/rexml/data/lang.xml11
-rw-r--r--test/rexml/data/lang0.xml18
-rw-r--r--test/rexml/data/message.xml27
-rw-r--r--test/rexml/data/moreover.xml244
-rw-r--r--test/rexml/data/much_ado.xml6850
-rw-r--r--test/rexml/data/namespaces.xml18
-rw-r--r--test/rexml/data/nitf.xml67
-rw-r--r--test/rexml/data/numbers.xml18
-rw-r--r--test/rexml/data/ofbiz-issues-full-177.xml13971
-rw-r--r--test/rexml/data/pi.xml13
-rw-r--r--test/rexml/data/pi2.xml6
-rw-r--r--test/rexml/data/project.xml1
-rw-r--r--test/rexml/data/simple.xml2
-rw-r--r--test/rexml/data/stream_accents.xml4
-rw-r--r--test/rexml/data/t63-1.xmlbin161690 -> 0 bytes-rw-r--r--test/rexml/data/t63-2.svg2828
-rw-r--r--test/rexml/data/t75.xml31
-rw-r--r--test/rexml/data/test/tests.xml683
-rw-r--r--test/rexml/data/test/tests.xsl369
-rw-r--r--test/rexml/data/testNamespaces.xml22
-rw-r--r--test/rexml/data/testsrc.xml64
-rw-r--r--test/rexml/data/text.xml10
-rw-r--r--test/rexml/data/ticket_61.xml4
-rw-r--r--test/rexml/data/ticket_68.xml590
-rw-r--r--test/rexml/data/tutorial.xml678
-rw-r--r--test/rexml/data/underscore.xml6
-rw-r--r--test/rexml/data/utf16.xmlbin207464 -> 0 bytes-rw-r--r--test/rexml/data/web.xml42
-rw-r--r--test/rexml/data/web2.xml7
-rw-r--r--test/rexml/data/working.rss202
-rw-r--r--test/rexml/data/xmlfile-bug.xml15
-rw-r--r--test/rexml/data/xp.tst27
-rw-r--r--test/rexml/data/yahoo.xml80
-rw-r--r--test/rexml/listener.rb51
-rw-r--r--test/rexml/parse/test_document_type_declaration.rb50
-rw-r--r--test/rexml/parse/test_notation_declaration.rb100
-rw-r--r--test/rexml/parser/test_sax2.rb203
-rw-r--r--test/rexml/parser/test_stream.rb32
-rw-r--r--test/rexml/parser/test_tree.rb43
-rw-r--r--test/rexml/parser/test_ultra_light.rb70
-rw-r--r--test/rexml/rexml_test_utils.rb7
-rw-r--r--test/rexml/test_attributes.rb223
-rw-r--r--test/rexml/test_attributes_mixin.rb32
-rw-r--r--test/rexml/test_changing_encoding.rb45
-rw-r--r--test/rexml/test_comment.rb26
-rw-r--r--test/rexml/test_contrib.rb585
-rw-r--r--test/rexml/test_core.rb1468
-rw-r--r--test/rexml/test_doctype.rb107
-rw-r--r--test/rexml/test_document.rb416
-rw-r--r--test/rexml/test_element.rb18
-rw-r--r--test/rexml/test_elements.rb119
-rw-r--r--test/rexml/test_encoding.rb108
-rw-r--r--test/rexml/test_entity.rb206
-rw-r--r--test/rexml/test_functions.rb238
-rw-r--r--test/rexml/test_functions_number.rb35
-rw-r--r--test/rexml/test_jaxen.rb131
-rw-r--r--test/rexml/test_light.rb107
-rw-r--r--test/rexml/test_lightparser.rb16
-rw-r--r--test/rexml/test_listener.rb131
-rw-r--r--test/rexml/test_martin_fowler.rb40
-rw-r--r--test/rexml/test_namespace.rb41
-rw-r--r--test/rexml/test_order.rb110
-rw-r--r--test/rexml/test_preceding_sibling.rb41
-rw-r--r--test/rexml/test_pullparser.rb103
-rw-r--r--test/rexml/test_rexml_issuezilla.rb19
-rw-r--r--test/rexml/test_sax.rb287
-rw-r--r--test/rexml/test_stream.rb130
-rw-r--r--test/rexml/test_text.rb22
-rw-r--r--test/rexml/test_ticket_80.rb59
-rw-r--r--test/rexml/test_validation_rng.rb793
-rw-r--r--test/rexml/test_xml_declaration.rb36
-rw-r--r--test/rexml/xpath/test_attribute.rb30
-rw-r--r--test/rexml/xpath/test_axis_preceding_sibling.rb40
-rw-r--r--test/rexml/xpath/test_base.rb1121
-rw-r--r--test/rexml/xpath/test_node.rb43
-rw-r--r--test/rexml/xpath/test_predicate.rb83
-rw-r--r--test/rexml/xpath/test_text.rb77
-rw-r--r--test/rinda/test_rinda.rb120
-rw-r--r--test/ripper/assert_parse_files.rb32
-rw-r--r--test/ripper/dummyparser.rb6
-rw-r--r--test/ripper/test_files.rb50
-rw-r--r--test/ripper/test_files_ext.rb8
-rw-r--r--test/ripper/test_files_lib.rb14
-rw-r--r--test/ripper/test_files_sample.rb8
-rw-r--r--test/ripper/test_files_test.rb8
-rw-r--r--test/ripper/test_files_test_1.rb9
-rw-r--r--test/ripper/test_files_test_2.rb9
-rw-r--r--test/ripper/test_lexer.rb221
-rw-r--r--test/ripper/test_parser_events.rb251
-rw-r--r--test/ripper/test_ripper.rb41
-rw-r--r--test/ripper/test_scanner_events.rb107
-rw-r--r--test/ripper/test_sexp.rb402
-rw-r--r--test/rss/dot.pngbin111 -> 0 bytes-rw-r--r--test/rss/rss-assertions.rb2091
-rw-r--r--test/rss/rss-testcase.rb479
-rw-r--r--test/rss/test_1.0.rb308
-rw-r--r--test/rss/test_2.0.rb412
-rw-r--r--test/rss/test_accessor.rb104
-rw-r--r--test/rss/test_atom.rb684
-rw-r--r--test/rss/test_content.rb105
-rw-r--r--test/rss/test_dublincore.rb270
-rw-r--r--test/rss/test_image.rb215
-rw-r--r--test/rss/test_inherit.rb41
-rw-r--r--test/rss/test_itunes.rb352
-rw-r--r--test/rss/test_maker_0.9.rb477
-rw-r--r--test/rss/test_maker_1.0.rb519
-rw-r--r--test/rss/test_maker_2.0.rb758
-rw-r--r--test/rss/test_maker_atom_entry.rb394
-rw-r--r--test/rss/test_maker_atom_feed.rb455
-rw-r--r--test/rss/test_maker_content.rb48
-rw-r--r--test/rss/test_maker_dc.rb150
-rw-r--r--test/rss/test_maker_image.rb63
-rw-r--r--test/rss/test_maker_itunes.rb480
-rw-r--r--test/rss/test_maker_slash.rb38
-rw-r--r--test/rss/test_maker_sy.rb45
-rw-r--r--test/rss/test_maker_taxo.rb82
-rw-r--r--test/rss/test_maker_trackback.rb42
-rw-r--r--test/rss/test_maker_xml-stylesheet.rb84
-rw-r--r--test/rss/test_parser.rb121
-rw-r--r--test/rss/test_parser_1.0.rb529
-rw-r--r--test/rss/test_parser_2.0.rb123
-rw-r--r--test/rss/test_parser_atom_entry.rb164
-rw-r--r--test/rss/test_parser_atom_feed.rb277
-rw-r--r--test/rss/test_setup_maker_0.9.rb247
-rw-r--r--test/rss/test_setup_maker_1.0.rb551
-rw-r--r--test/rss/test_setup_maker_2.0.rb309
-rw-r--r--test/rss/test_setup_maker_atom_entry.rb410
-rw-r--r--test/rss/test_setup_maker_atom_feed.rb446
-rw-r--r--test/rss/test_setup_maker_itunes.rb144
-rw-r--r--test/rss/test_setup_maker_slash.rb39
-rw-r--r--test/rss/test_slash.rb65
-rw-r--r--test/rss/test_syndication.rb126
-rw-r--r--test/rss/test_taxonomy.rb173
-rw-r--r--test/rss/test_to_s.rb701
-rw-r--r--test/rss/test_trackback.rb136
-rw-r--r--test/rss/test_version.rb10
-rw-r--r--test/rss/test_xml-stylesheet.rb109
-rw-r--r--test/ruby/enc/test_case_comprehensive.rb8
-rw-r--r--test/ruby/enc/test_case_mapping.rb33
-rw-r--r--test/ruby/enc/test_cesu8.rb113
-rw-r--r--test/ruby/enc/test_emoji_breaks.rb155
-rw-r--r--test/ruby/enc/test_grapheme_breaks.rb92
-rw-r--r--test/ruby/enc/test_regex_casefold.rb4
-rw-r--r--test/ruby/marshaltestlib.rb2
-rw-r--r--test/ruby/sentence.rb2
-rw-r--r--test/ruby/test_alias.rb67
-rw-r--r--test/ruby/test_argf.rb139
-rw-r--r--test/ruby/test_arithmetic_sequence.rb72
-rw-r--r--test/ruby/test_arity.rb44
-rw-r--r--test/ruby/test_array.rb900
-rw-r--r--test/ruby/test_assignment.rb199
-rw-r--r--test/ruby/test_ast.rb907
-rw-r--r--test/ruby/test_autoload.rb266
-rw-r--r--test/ruby/test_backtrace.rb131
-rw-r--r--test/ruby/test_basicinstructions.rb5
-rw-r--r--test/ruby/test_beginendblock.rb22
-rw-r--r--test/ruby/test_bignum.rb112
-rw-r--r--test/ruby/test_call.rb16
-rw-r--r--test/ruby/test_case.rb12
-rw-r--r--test/ruby/test_class.rb238
-rw-r--r--test/ruby/test_clone.rb53
-rw-r--r--test/ruby/test_comparable.rb32
-rw-r--r--test/ruby/test_complex.rb250
-rw-r--r--test/ruby/test_complex2.rb2
-rw-r--r--test/ruby/test_complexrational.rb12
-rw-r--r--test/ruby/test_const.rb39
-rw-r--r--test/ruby/test_continuation.rb17
-rw-r--r--test/ruby/test_data.rb249
-rw-r--r--test/ruby/test_default_gems.rb1
-rw-r--r--test/ruby/test_defined.rb161
-rw-r--r--test/ruby/test_dir.rb345
-rw-r--r--test/ruby/test_dir_m17n.rb75
-rw-r--r--test/ruby/test_dup.rb110
-rw-r--r--test/ruby/test_econv.rb27
-rw-r--r--test/ruby/test_encoding.rb63
-rw-r--r--test/ruby/test_enum.rb259
-rw-r--r--test/ruby/test_enumerator.rb375
-rw-r--r--test/ruby/test_env.rb980
-rw-r--r--test/ruby/test_eval.rb95
-rw-r--r--test/ruby/test_exception.rb694
-rw-r--r--test/ruby/test_fiber.rb227
-rw-r--r--test/ruby/test_file.rb153
-rw-r--r--test/ruby/test_file_exhaustive.rb299
-rw-r--r--test/ruby/test_fixnum.rb1
-rw-r--r--test/ruby/test_flip.rb17
-rw-r--r--test/ruby/test_float.rb66
-rw-r--r--test/ruby/test_fnmatch.rb37
-rw-r--r--test/ruby/test_frozen.rb30
-rw-r--r--test/ruby/test_frozen_error.rb57
-rw-r--r--test/ruby/test_gc.rb257
-rw-r--r--test/ruby/test_gc_compact.rb456
-rw-r--r--test/ruby/test_hash.rb769
-rw-r--r--test/ruby/test_inlinecache.rb110
-rw-r--r--test/ruby/test_insns_leaf.rb46
-rw-r--r--test/ruby/test_integer.rb178
-rw-r--r--test/ruby/test_io.rb645
-rw-r--r--test/ruby/test_io_buffer.rb562
-rw-r--r--test/ruby/test_io_m17n.rb93
-rw-r--r--test/ruby/test_io_timeout.rb58
-rw-r--r--test/ruby/test_iseq.rb412
-rw-r--r--test/ruby/test_iterator.rb20
-rw-r--r--test/ruby/test_jit.rb897
-rw-r--r--test/ruby/test_keyword.rb3717
-rw-r--r--test/ruby/test_lambda.rb171
-rw-r--r--test/ruby/test_lazy_enumerator.rb132
-rw-r--r--test/ruby/test_literal.rb101
-rw-r--r--test/ruby/test_m17n.rb116
-rw-r--r--test/ruby/test_m17n_comb.rb42
-rw-r--r--test/ruby/test_marshal.rb334
-rw-r--r--test/ruby/test_math.rb54
-rw-r--r--test/ruby/test_memory_view.rb341
-rw-r--r--test/ruby/test_method.rb642
-rw-r--r--test/ruby/test_method_cache.rb76
-rw-r--r--test/ruby/test_mjit.rb1327
-rw-r--r--test/ruby/test_mjit_debug.rb17
-rw-r--r--test/ruby/test_module.rb1185
-rw-r--r--test/ruby/test_name_error.rb156
-rw-r--r--test/ruby/test_nomethod_error.rb109
-rw-r--r--test/ruby/test_notimp.rb85
-rw-r--r--test/ruby/test_numeric.rb117
-rw-r--r--test/ruby/test_object.rb177
-rw-r--r--test/ruby/test_objectspace.rb78
-rw-r--r--test/ruby/test_optimization.rb205
-rw-r--r--test/ruby/test_pack.rb101
-rw-r--r--test/ruby/test_parse.rb743
-rw-r--r--test/ruby/test_path.rb8
-rw-r--r--test/ruby/test_pattern_matching.rb1713
-rw-r--r--test/ruby/test_primitive.rb78
-rw-r--r--test/ruby/test_proc.rb580
-rw-r--r--test/ruby/test_process.rb488
-rw-r--r--test/ruby/test_rand.rb362
-rw-r--r--test/ruby/test_random_formatter.rb123
-rw-r--r--test/ruby/test_range.rb240
-rw-r--r--test/ruby/test_rational.rb51
-rw-r--r--test/ruby/test_refinement.rb698
-rw-r--r--test/ruby/test_regexp.rb673
-rw-r--r--test/ruby/test_require.rb347
-rw-r--r--test/ruby/test_require_lib.rb27
-rw-r--r--test/ruby/test_rubyoptions.rb289
-rw-r--r--test/ruby/test_rubyvm.rb57
-rw-r--r--test/ruby/test_rubyvm_mjit.rb56
-rw-r--r--test/ruby/test_settracefunc.rb1136
-rw-r--r--test/ruby/test_shapes.rb441
-rw-r--r--test/ruby/test_signal.rb12
-rw-r--r--test/ruby/test_sprintf.rb40
-rw-r--r--test/ruby/test_stack.rb82
-rw-r--r--test/ruby/test_string.rb1205
-rw-r--r--test/ruby/test_struct.rb146
-rw-r--r--test/ruby/test_super.rb166
-rw-r--r--test/ruby/test_symbol.rb119
-rw-r--r--test/ruby/test_syntax.rb873
-rw-r--r--test/ruby/test_system.rb4
-rw-r--r--test/ruby/test_thread.rb263
-rw-r--r--test/ruby/test_thread_cv.rb65
-rw-r--r--test/ruby/test_thread_queue.rb192
-rw-r--r--test/ruby/test_time.rb355
-rw-r--r--test/ruby/test_time_tz.rb386
-rw-r--r--test/ruby/test_trace.rb11
-rw-r--r--test/ruby/test_transcode.rb92
-rw-r--r--test/ruby/test_undef.rb18
-rw-r--r--test/ruby/test_variable.rb174
-rw-r--r--test/ruby/test_vm_dump.rb6
-rw-r--r--test/ruby/test_weakmap.rb94
-rw-r--r--test/ruby/test_yjit.rb1333
-rw-r--r--test/ruby/test_yjit_exit_locations.rb110
-rw-r--r--test/rubygems/alternate_cert.pem28
-rw-r--r--test/rubygems/alternate_cert_32.pem30
-rw-r--r--test/rubygems/alternate_key.pem50
-rw-r--r--test/rubygems/bad_rake.rb1
-rw-r--r--test/rubygems/bogussources.rb9
-rw-r--r--test/rubygems/bundler_test_gem.rb421
-rw-r--r--test/rubygems/ca_cert.pem139
-rw-r--r--test/rubygems/child_cert.pem31
-rw-r--r--test/rubygems/child_cert_32.pem31
-rw-r--r--test/rubygems/child_key.pem50
-rw-r--r--test/rubygems/client.pem148
-rw-r--r--test/rubygems/data/excon-0.7.7.gemspec.rzbin0 -> 388 bytes-rw-r--r--test/rubygems/data/null-required-ruby-version.gemspec.rzbin0 -> 403 bytes-rw-r--r--test/rubygems/data/null-required-rubygems-version.gemspec.rzbin0 -> 421 bytes-rw-r--r--test/rubygems/data/null-type.gemspec.rzbin554 -> 0 bytes-rw-r--r--test/rubygems/data/pry-0.4.7.gemspec.rzbin0 -> 433 bytes-rw-r--r--test/rubygems/encrypted_private_key.pem52
-rw-r--r--test/rubygems/expired_cert.pem30
-rw-r--r--test/rubygems/fake_certlib/openssl.rb1
-rw-r--r--test/rubygems/future_cert.pem30
-rw-r--r--test/rubygems/future_cert_32.pem30
-rw-r--r--test/rubygems/good_rake.rb1
-rw-r--r--test/rubygems/grandchild_cert.pem31
-rw-r--r--test/rubygems/grandchild_cert_32.pem31
-rw-r--r--test/rubygems/grandchild_key.pem50
-rw-r--r--test/rubygems/helper.rb1643
-rw-r--r--test/rubygems/installer_test_case.rb248
-rw-r--r--test/rubygems/invalid_issuer_cert.pem32
-rw-r--r--test/rubygems/invalid_issuer_cert_32.pem32
-rw-r--r--test/rubygems/invalid_key.pem50
-rw-r--r--test/rubygems/invalid_signer_cert.pem30
-rw-r--r--test/rubygems/invalid_signer_cert_32.pem30
-rw-r--r--test/rubygems/invalidchild_cert.pem31
-rw-r--r--test/rubygems/invalidchild_cert_32.pem31
-rw-r--r--test/rubygems/invalidchild_key.pem50
-rw-r--r--test/rubygems/multifactor_auth_utilities.rb111
-rw-r--r--test/rubygems/package/tar_test_case.rb175
-rw-r--r--test/rubygems/packages/Bluebie-legs-0.6.2.gembin0 -> 14336 bytes-rw-r--r--test/rubygems/packages/ascii_binder-0.1.10.1.gembin0 -> 244736 bytes-rw-r--r--test/rubygems/packages/ill-formatted-platform-1.0.0.10.gembin0 -> 10240 bytes-rw-r--r--test/rubygems/plugin/exception/rubygems_plugin.rb3
-rw-r--r--test/rubygems/plugin/load/rubygems_plugin.rb1
-rw-r--r--test/rubygems/plugin/standarderror/rubygems_plugin.rb3
-rw-r--r--test/rubygems/private_ec_key.pem9
-rw-r--r--test/rubygems/private_key.pem50
-rw-r--r--test/rubygems/public_cert.pem32
-rw-r--r--test/rubygems/public_cert_32.pem30
-rw-r--r--test/rubygems/public_key.pem14
-rw-r--r--test/rubygems/rubygems/commands/crash_command.rb3
-rw-r--r--test/rubygems/rubygems_plugin.rb8
-rw-r--r--test/rubygems/simple_gem.rb1
-rw-r--r--test/rubygems/specifications/bar-0.0.2.gemspec2
-rw-r--r--test/rubygems/specifications/rubyforge-0.0.1.gemspec14
-rw-r--r--test/rubygems/ssl_cert.pem95
-rw-r--r--test/rubygems/ssl_key.pem38
-rw-r--r--test/rubygems/test_bundled_ca.rb92
-rw-r--r--test/rubygems/test_config.rb20
-rw-r--r--test/rubygems/test_deprecate.rb101
-rw-r--r--test/rubygems/test_exit.rb17
-rw-r--r--test/rubygems/test_gem.rb1601
-rw-r--r--test/rubygems/test_gem_available_set.rb50
-rw-r--r--test/rubygems/test_gem_bundler_version_finder.rb86
-rw-r--r--test/rubygems/test_gem_command.rb227
-rw-r--r--test/rubygems/test_gem_command_manager.rb208
-rw-r--r--test/rubygems/test_gem_commands_build_command.rb570
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb370
-rw-r--r--test/rubygems/test_gem_commands_check_command.rb23
-rw-r--r--test/rubygems/test_gem_commands_cleanup_command.rb151
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb119
-rw-r--r--test/rubygems/test_gem_commands_dependency_command.rb82
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb109
-rw-r--r--test/rubygems/test_gem_commands_exec_command.rb853
-rw-r--r--test/rubygems/test_gem_commands_fetch_command.rb197
-rw-r--r--test/rubygems/test_gem_commands_generate_index_command.rb48
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb56
-rw-r--r--test/rubygems/test_gem_commands_info_command.rb44
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb796
-rw-r--r--test/rubygems/test_gem_commands_list_command.rb13
-rw-r--r--test/rubygems/test_gem_commands_lock_command.rb26
-rw-r--r--test/rubygems/test_gem_commands_mirror.rb8
-rw-r--r--test/rubygems/test_gem_commands_open_command.rb36
-rw-r--r--test/rubygems/test_gem_commands_outdated_command.rb33
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb344
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb378
-rw-r--r--test/rubygems/test_gem_commands_push_command.rb364
-rw-r--r--test/rubygems/test_gem_commands_query_command.rb204
-rw-r--r--test/rubygems/test_gem_commands_search_command.rb8
-rw-r--r--test/rubygems/test_gem_commands_server_command.rb54
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb489
-rw-r--r--test/rubygems/test_gem_commands_signin_command.rb215
-rw-r--r--test/rubygems/test_gem_commands_signout_command.rb13
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb301
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb138
-rw-r--r--test/rubygems/test_gem_commands_stale_command.rb11
-rw-r--r--test/rubygems/test_gem_commands_uninstall_command.rb383
-rw-r--r--test/rubygems/test_gem_commands_unpack_command.rb68
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb515
-rw-r--r--test/rubygems/test_gem_commands_which_command.rb28
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb255
-rw-r--r--test/rubygems/test_gem_config_file.rb223
-rw-r--r--test/rubygems/test_gem_dependency.rb182
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb731
-rw-r--r--test/rubygems/test_gem_dependency_list.rb129
-rw-r--r--test/rubygems/test_gem_dependency_resolution_error.rb14
-rw-r--r--test/rubygems/test_gem_doctor.rb124
-rw-r--r--test/rubygems/test_gem_ext_builder.rb225
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder.rb167
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/.gitignore1
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/custom_name.gemspec10
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock233
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml10
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/src/lib.rs27
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/custom_name/lib/custom_name.rb3
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/.gitignore1
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock247
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml10
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/rust_ruby_example.gemspec10
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/src/lib.rs51
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder_link_flag_converter.rb34
-rw-r--r--test/rubygems/test_gem_ext_cargo_builder_unit.rb60
-rw-r--r--test/rubygems/test_gem_ext_cmake_builder.rb72
-rw-r--r--test/rubygems/test_gem_ext_configure_builder.rb55
-rw-r--r--test/rubygems/test_gem_ext_ext_conf_builder.rb158
-rw-r--r--test/rubygems/test_gem_ext_rake_builder.rb85
-rw-r--r--test/rubygems/test_gem_gem_runner.rb68
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb277
-rw-r--r--test/rubygems/test_gem_impossible_dependencies_error.rb12
-rw-r--r--test/rubygems/test_gem_indexer.rb225
-rw-r--r--test/rubygems/test_gem_install_update_options.rb112
-rw-r--r--test/rubygems/test_gem_installer.rb1947
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb26
-rw-r--r--test/rubygems/test_gem_name_tuple.rb12
-rw-r--r--test/rubygems/test_gem_package.rb687
-rw-r--r--test/rubygems/test_gem_package_old.rb115
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb160
-rw-r--r--test/rubygems/test_gem_package_tar_reader.rb65
-rw-r--r--test/rubygems/test_gem_package_tar_reader_entry.rb193
-rw-r--r--test/rubygems/test_gem_package_tar_writer.rb213
-rw-r--r--test/rubygems/test_gem_package_task.rb84
-rw-r--r--test/rubygems/test_gem_path_support.rb63
-rw-r--r--test/rubygems/test_gem_platform.rb597
-rw-r--r--test/rubygems/test_gem_rdoc.rb177
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb911
-rw-r--r--test/rubygems/test_gem_request.rb233
-rw-r--r--test/rubygems/test_gem_request_connection_pools.rb67
-rw-r--r--test/rubygems/test_gem_request_set.rb297
-rw-r--r--test/rubygems/test_gem_request_set_gem_dependency_api.rb551
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb191
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_parser.rb144
-rw-r--r--test/rubygems/test_gem_request_set_lockfile_tokenizer.rb275
-rw-r--r--test/rubygems/test_gem_requirement.rb210
-rw-r--r--test/rubygems/test_gem_resolver.rb342
-rw-r--r--test/rubygems/test_gem_resolver_activation_request.rb51
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb159
-rw-r--r--test/rubygems/test_gem_resolver_api_specification.rb98
-rw-r--r--test/rubygems/test_gem_resolver_best_set.rb67
-rw-r--r--test/rubygems/test_gem_resolver_composed_set.rb6
-rw-r--r--test/rubygems/test_gem_resolver_conflict.rb32
-rw-r--r--test/rubygems/test_gem_resolver_dependency_request.rb34
-rw-r--r--test/rubygems/test_gem_resolver_git_set.rb48
-rw-r--r--test/rubygems/test_gem_resolver_git_specification.rb46
-rw-r--r--test/rubygems/test_gem_resolver_index_set.rb32
-rw-r--r--test/rubygems/test_gem_resolver_index_specification.rb41
-rw-r--r--test/rubygems/test_gem_resolver_installed_specification.rb15
-rw-r--r--test/rubygems/test_gem_resolver_installer_set.rb152
-rw-r--r--test/rubygems/test_gem_resolver_local_specification.rb18
-rw-r--r--test/rubygems/test_gem_resolver_lock_set.rb34
-rw-r--r--test/rubygems/test_gem_resolver_lock_specification.rb38
-rw-r--r--test/rubygems/test_gem_resolver_requirement_list.rb6
-rw-r--r--test/rubygems/test_gem_resolver_specification.rb22
-rw-r--r--test/rubygems/test_gem_resolver_vendor_set.rb22
-rw-r--r--test/rubygems/test_gem_resolver_vendor_specification.rb24
-rw-r--r--test/rubygems/test_gem_security.rb190
-rw-r--r--test/rubygems/test_gem_security_policy.rb216
-rw-r--r--test/rubygems/test_gem_security_signer.rb105
-rw-r--r--test/rubygems/test_gem_security_trust_dir.rb32
-rw-r--r--test/rubygems/test_gem_server.rb607
-rw-r--r--test/rubygems/test_gem_silent_ui.rb90
-rw-r--r--test/rubygems/test_gem_source.rb131
-rw-r--r--test/rubygems/test_gem_source_fetch_problem.rb27
-rw-r--r--test/rubygems/test_gem_source_git.rb157
-rw-r--r--test/rubygems/test_gem_source_installed.rb36
-rw-r--r--test/rubygems/test_gem_source_list.rb11
-rw-r--r--test/rubygems/test_gem_source_local.rb32
-rw-r--r--test/rubygems/test_gem_source_lock.rb66
-rw-r--r--test/rubygems/test_gem_source_specific_file.rb38
-rw-r--r--test/rubygems/test_gem_source_subpath_problem.rb50
-rw-r--r--test/rubygems/test_gem_source_vendor.rb30
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb143
-rw-r--r--test/rubygems/test_gem_specification.rb2195
-rw-r--r--test/rubygems/test_gem_stream_ui.rb102
-rw-r--r--test/rubygems/test_gem_stub_specification.rb98
-rw-r--r--test/rubygems/test_gem_text.rb15
-rw-r--r--test/rubygems/test_gem_uninstaller.rb370
-rw-r--r--test/rubygems/test_gem_unsatisfiable_dependency_error.rb10
-rw-r--r--test/rubygems/test_gem_update_suggestion.rb209
-rw-r--r--test/rubygems/test_gem_uri.rb41
-rw-r--r--test/rubygems/test_gem_uri_formatter.rb32
-rw-r--r--test/rubygems/test_gem_util.rb82
-rw-r--r--test/rubygems/test_gem_validator.rb28
-rw-r--r--test/rubygems/test_gem_version.rb108
-rw-r--r--test/rubygems/test_gem_version_option.rb36
-rw-r--r--test/rubygems/test_kernel.rb121
-rw-r--r--test/rubygems/test_project_sanity.rb49
-rw-r--r--test/rubygems/test_remote_fetch_error.rb17
-rw-r--r--test/rubygems/test_require.rb529
-rw-r--r--test/rubygems/test_rubygems.rb76
-rw-r--r--test/rubygems/test_webauthn_listener.rb143
-rw-r--r--test/rubygems/test_webauthn_listener_response.rb93
-rw-r--r--test/rubygems/test_webauthn_poller.rb124
-rw-r--r--test/rubygems/utilities.rb435
-rw-r--r--test/rubygems/wrong_key_cert.pem30
-rw-r--r--test/rubygems/wrong_key_cert_32.pem30
-rw-r--r--test/runner.rb46
-rw-r--r--test/scanf/data.txt6
-rw-r--r--test/scanf/test_scanf.rb305
-rw-r--r--test/scanf/test_scanfblocks.rb82
-rw-r--r--test/scanf/test_scanfio.rb21
-rw-r--r--test/sdbm/test_sdbm.rb544
-rw-r--r--test/shell/test_command_processor.rb83
-rw-r--r--test/socket/test_addrinfo.rb22
-rw-r--r--test/socket/test_basicsocket.rb7
-rw-r--r--test/socket/test_nonblock.rb16
-rw-r--r--test/socket/test_socket.rb43
-rw-r--r--test/socket/test_sockopt.rb2
-rw-r--r--test/socket/test_tcp.rb45
-rw-r--r--test/socket/test_udp.rb10
-rw-r--r--test/socket/test_unix.rb167
-rw-r--r--test/stringio/test_ractor.rb23
-rw-r--r--test/stringio/test_stringio.rb206
-rw-r--r--test/strscan/test_ractor.rb28
-rw-r--r--test/strscan/test_stringscanner.rb369
-rw-r--r--test/syslog/test_syslog_logger.rb15
-rw-r--r--test/test_cmath.rb118
-rw-r--r--test/test_delegate.rb141
-rw-r--r--test/test_extlibs.rb4
-rw-r--r--test/test_find.rb16
-rw-r--r--test/test_forwardable.rb59
-rw-r--r--test/test_getoptlong.rb163
-rw-r--r--test/test_ipaddr.rb100
-rw-r--r--test/test_mutex_m.rb32
-rw-r--r--test/test_open3.rb32
-rw-r--r--test/test_pp.rb49
-rw-r--r--test/test_prime.rb279
-rw-r--r--test/test_pstore.rb38
-rw-r--r--test/test_pty.rb30
-rw-r--r--test/test_securerandom.rb123
-rw-r--r--test/test_set.rb206
-rw-r--r--test/test_shellwords.rb5
-rw-r--r--test/test_sorted_set.rb45
-rw-r--r--test/test_sync.rb69
-rw-r--r--test/test_syslog.rb9
-rw-r--r--test/test_tempfile.rb67
-rw-r--r--test/test_time.rb71
-rw-r--r--test/test_timeout.rb65
-rw-r--r--test/test_tmpdir.rb102
-rw-r--r--test/test_tracer.rb56
-rw-r--r--test/test_trick.rb214
-rw-r--r--test/test_unicode_normalize.rb6
-rw-r--r--test/test_win32api.rb24
-rw-r--r--test/testunit/test4test_hideskip.rb10
-rw-r--r--test/testunit/test4test_redefinition.rb14
-rw-r--r--test/testunit/test4test_sorting.rb18
-rw-r--r--test/testunit/test_assertion.rb29
-rw-r--r--test/testunit/test_hideskip.rb17
-rw-r--r--test/testunit/test_parallel.rb201
-rw-r--r--test/testunit/test_redefinition.rb16
-rw-r--r--test/testunit/test_sorting.rb18
-rw-r--r--test/testunit/tests_for_parallel/runner.rb14
-rw-r--r--test/uri/test_common.rb122
-rw-r--r--test/uri/test_generic.rb90
-rw-r--r--test/uri/test_http.rb31
-rw-r--r--test/uri/test_ldap.rb10
-rw-r--r--test/uri/test_parser.rb48
-rw-r--r--test/uri/test_ws.rb71
-rw-r--r--test/uri/test_wss.rb71
-rw-r--r--test/webrick/test_cgi.rb170
-rw-r--r--test/webrick/test_filehandler.rb351
-rw-r--r--test/webrick/test_httpauth.rb341
-rw-r--r--test/webrick/test_httpproxy.rb427
-rw-r--r--test/webrick/test_httprequest.rb425
-rw-r--r--test/webrick/test_httpresponse.rb226
-rw-r--r--test/webrick/test_httpserver.rb511
-rw-r--r--test/webrick/test_server.rb163
-rw-r--r--test/webrick/test_ssl_server.rb67
-rw-r--r--test/webrick/test_utils.rb110
-rw-r--r--test/webrick/utils.rb74
-rw-r--r--test/win32ole/available_ole.rb41
-rw-r--r--test/win32ole/test_err_in_callback.rb2
-rw-r--r--test/win32ole/test_win32ole.rb53
-rw-r--r--test/win32ole/test_win32ole_event.rb92
-rw-r--r--test/win32ole/test_win32ole_method.rb13
-rw-r--r--test/win32ole/test_win32ole_method_event.rb36
-rw-r--r--test/win32ole/test_win32ole_param.rb53
-rw-r--r--test/win32ole/test_win32ole_param_event.rb30
-rw-r--r--test/win32ole/test_win32ole_record.rb20
-rw-r--r--test/win32ole/test_win32ole_type.rb52
-rw-r--r--test/win32ole/test_win32ole_type_event.rb44
-rw-r--r--test/win32ole/test_win32ole_variable.rb4
-rw-r--r--test/win32ole/test_win32ole_variant.rb2
-rw-r--r--test/win32ole/test_win32ole_variant_outarg.rb2
-rw-r--r--test/win32ole/test_word.rb2
-rw-r--r--test/yaml/test_store.rb6
-rw-r--r--test/zlib/test_zlib.rb272
-rw-r--r--thread.c3348
-rw-r--r--thread_none.c284
-rw-r--r--thread_none.h20
-rw-r--r--thread_pthread.c1503
-rw-r--r--thread_pthread.h96
-rw-r--r--thread_sync.c948
-rw-r--r--thread_sync.rb68
-rw-r--r--thread_win32.c516
-rw-r--r--thread_win32.h45
-rw-r--r--time.c3939
-rw-r--r--timev.h20
-rw-r--r--timev.rb404
-rw-r--r--tool/annocheck/Dockerfile4
-rw-r--r--tool/annocheck/Dockerfile-copy7
-rwxr-xr-xtool/bisect.sh19
-rw-r--r--tool/bundler/dev_gems.rb19
-rw-r--r--tool/bundler/dev_gems.rb.lock57
-rw-r--r--tool/bundler/rubocop_gems.rb12
-rw-r--r--tool/bundler/rubocop_gems.rb.lock73
-rw-r--r--tool/bundler/standard_gems.rb12
-rw-r--r--tool/bundler/standard_gems.rb.lock81
-rw-r--r--tool/bundler/test_gems.rb12
-rw-r--r--tool/bundler/test_gems.rb.lock45
-rwxr-xr-xtool/change_maker.rb47
-rwxr-xr-xtool/checksum.rb6
-rw-r--r--tool/ci_functions.sh29
-rw-r--r--tool/colorize.rb41
-rw-r--r--tool/colors (renamed from test/colors)0
-rwxr-xr-xtool/darwin-cc6
-rwxr-xr-xtool/disable_ipv6.sh9
-rw-r--r--tool/downloader.rb163
-rwxr-xr-xtool/enc-case-folding.rb416
-rw-r--r--tool/enc-emoji-citrus-gen.rb6
-rwxr-xr-xtool/enc-unicode.rb77
-rwxr-xr-xtool/expand-config.rb14
-rwxr-xr-xtool/extlibs.rb209
-rw-r--r--tool/fake.rb48
-rwxr-xr-xtool/fetch-bundled_gems.rb24
-rwxr-xr-xtool/file2lastrev.rb101
-rwxr-xr-xtool/format-release262
-rwxr-xr-xtool/gem-unpack.rb18
-rwxr-xr-xtool/gen-mailmap.rb47
-rw-r--r--tool/generate-backport-changelog.rb99
-rw-r--r--tool/generic_erb.rb45
-rw-r--r--tool/gperf.sed1
-rwxr-xr-xtool/id2token.rb12
-rwxr-xr-xtool/ifchange29
-rwxr-xr-xtool/intern_ids.rb35
-rwxr-xr-xtool/leaked-globals65
-rw-r--r--tool/lib/-test-/integer.rb14
-rw-r--r--tool/lib/bundled_gem.rb68
-rw-r--r--tool/lib/colorize.rb55
-rw-r--r--tool/lib/core_assertions.rb821
-rw-r--r--tool/lib/envutil.rb380
-rw-r--r--tool/lib/find_executable.rb (renamed from test/lib/find_executable.rb)0
-rw-r--r--tool/lib/gc_checker.rb36
-rw-r--r--tool/lib/iseq_loader_checker.rb81
-rw-r--r--tool/lib/jisx0208.rb (renamed from tool/jisx0208.rb)0
-rw-r--r--tool/lib/leakchecker.rb321
-rw-r--r--tool/lib/memory_status.rb151
-rw-r--r--tool/lib/output.rb57
-rw-r--r--tool/lib/profile_test_all.rb91
-rw-r--r--tool/lib/test/unit.rb1822
-rw-r--r--tool/lib/test/unit/assertions.rb833
-rw-r--r--tool/lib/test/unit/parallel.rb212
-rw-r--r--tool/lib/test/unit/testcase.rb296
-rw-r--r--tool/lib/tracepointchecker.rb126
-rw-r--r--tool/lib/vcs.rb845
-rw-r--r--tool/lib/vpath.rb92
-rw-r--r--tool/lib/webrick.rb232
-rw-r--r--tool/lib/webrick/.document (renamed from lib/webrick/.document)0
-rw-r--r--tool/lib/webrick/accesslog.rb157
-rw-r--r--tool/lib/webrick/cgi.rb313
-rw-r--r--tool/lib/webrick/compat.rb (renamed from lib/webrick/compat.rb)0
-rw-r--r--tool/lib/webrick/config.rb158
-rw-r--r--tool/lib/webrick/cookie.rb172
-rw-r--r--tool/lib/webrick/htmlutils.rb (renamed from lib/webrick/htmlutils.rb)0
-rw-r--r--tool/lib/webrick/httpauth.rb96
-rw-r--r--tool/lib/webrick/httpauth/authenticator.rb117
-rw-r--r--tool/lib/webrick/httpauth/basicauth.rb116
-rw-r--r--tool/lib/webrick/httpauth/digestauth.rb395
-rw-r--r--tool/lib/webrick/httpauth/htdigest.rb132
-rw-r--r--tool/lib/webrick/httpauth/htgroup.rb (renamed from lib/webrick/httpauth/htgroup.rb)0
-rw-r--r--tool/lib/webrick/httpauth/htpasswd.rb158
-rw-r--r--tool/lib/webrick/httpauth/userdb.rb (renamed from lib/webrick/httpauth/userdb.rb)0
-rw-r--r--tool/lib/webrick/httpproxy.rb354
-rw-r--r--tool/lib/webrick/httprequest.rb636
-rw-r--r--tool/lib/webrick/httpresponse.rb564
-rw-r--r--tool/lib/webrick/https.rb152
-rw-r--r--tool/lib/webrick/httpserver.rb293
-rw-r--r--tool/lib/webrick/httpservlet.rb23
-rw-r--r--tool/lib/webrick/httpservlet/abstract.rb152
-rw-r--r--tool/lib/webrick/httpservlet/cgi_runner.rb (renamed from lib/webrick/httpservlet/cgi_runner.rb)0
-rw-r--r--tool/lib/webrick/httpservlet/cgihandler.rb126
-rw-r--r--tool/lib/webrick/httpservlet/erbhandler.rb88
-rw-r--r--tool/lib/webrick/httpservlet/filehandler.rb552
-rw-r--r--tool/lib/webrick/httpservlet/prochandler.rb47
-rw-r--r--tool/lib/webrick/httpstatus.rb194
-rw-r--r--tool/lib/webrick/httputils.rb512
-rw-r--r--tool/lib/webrick/httpversion.rb (renamed from lib/webrick/httpversion.rb)0
-rw-r--r--tool/lib/webrick/log.rb (renamed from lib/webrick/log.rb)0
-rw-r--r--tool/lib/webrick/server.rb381
-rw-r--r--tool/lib/webrick/ssl.rb215
-rw-r--r--tool/lib/webrick/utils.rb265
-rw-r--r--tool/lib/webrick/version.rb18
-rw-r--r--tool/lib/zombie_hunter.rb (renamed from test/lib/zombie_hunter.rb)0
-rwxr-xr-xtool/ln_sr.rb131
-rw-r--r--tool/m4/_colorize_result_prepare.m411
-rw-r--r--tool/m4/ac_msg_result.m42
-rw-r--r--tool/m4/colorize_result.m42
-rw-r--r--tool/m4/ruby_append_option.m42
-rw-r--r--tool/m4/ruby_append_options.m42
-rw-r--r--tool/m4/ruby_check_builtin_func.m42
-rw-r--r--tool/m4/ruby_check_builtin_setjmp.m410
-rw-r--r--tool/m4/ruby_check_printf_prefix.m411
-rw-r--r--tool/m4/ruby_check_setjmp.m410
-rw-r--r--tool/m4/ruby_check_signedness.m42
-rw-r--r--tool/m4/ruby_check_sizeof.m42
-rw-r--r--tool/m4/ruby_check_sysconf.m48
-rw-r--r--tool/m4/ruby_cppoutfile.m46
-rw-r--r--tool/m4/ruby_decl_attribute.m47
-rw-r--r--tool/m4/ruby_default_arch.m47
-rw-r--r--tool/m4/ruby_define_if.m412
-rw-r--r--tool/m4/ruby_defint.m42
-rw-r--r--tool/m4/ruby_dtrace_available.m44
-rw-r--r--tool/m4/ruby_dtrace_postprocess.m44
-rw-r--r--tool/m4/ruby_func_attribute.m42
-rw-r--r--tool/m4/ruby_mingw32.m46
-rw-r--r--tool/m4/ruby_prepend_option.m42
-rw-r--r--tool/m4/ruby_prog_gnu_ld.m42
-rw-r--r--tool/m4/ruby_prog_makedirs.m49
-rw-r--r--tool/m4/ruby_replace_funcs.m413
-rw-r--r--tool/m4/ruby_replace_type.m414
-rw-r--r--tool/m4/ruby_require_funcs.m413
-rw-r--r--tool/m4/ruby_rm_recursive.m46
-rw-r--r--tool/m4/ruby_setjmp_type.m44
-rw-r--r--tool/m4/ruby_stack_grow_direction.m48
-rw-r--r--tool/m4/ruby_thread.m480
-rw-r--r--tool/m4/ruby_try_cflags.m411
-rw-r--r--tool/m4/ruby_try_cxxflags.m417
-rw-r--r--tool/m4/ruby_try_ldflags.m44
-rw-r--r--tool/m4/ruby_type_attribute.m42
-rw-r--r--tool/m4/ruby_universal_arch.m444
-rw-r--r--tool/m4/ruby_wasm_tools.m424
-rw-r--r--tool/m4/ruby_werror_flag.m42
-rwxr-xr-xtool/make-snapshot424
-rw-r--r--tool/make_hgraph.rb7
-rwxr-xr-xtool/merger.rb429
-rwxr-xr-xtool/mjit/bindgen.rb435
-rw-r--r--tool/mjit_archflag.sh40
-rw-r--r--tool/mjit_tabs.rb67
-rw-r--r--tool/mk_builtin_loader.rb411
-rw-r--r--tool/mk_call_iseq_optimized.rb73
-rwxr-xr-xtool/mkconfig.rb102
-rwxr-xr-xtool/mkrunnable.rb28
-rwxr-xr-xtool/outdate-bundled-gems.rb135
-rw-r--r--tool/prereq.status3
-rwxr-xr-xtool/rbinstall.rb564
-rw-r--r--tool/rbs_skip_tests11
-rwxr-xr-xtool/rbuninstall.rb18
-rwxr-xr-xtool/redmine-backporter.rb221
-rwxr-xr-xtool/release.sh47
-rwxr-xr-xtool/releng/gen-mail.rb55
-rwxr-xr-xtool/releng/gen-release-note.rb36
-rwxr-xr-xtool/releng/update-www-meta.rb213
-rw-r--r--tool/ruby_vm/controllers/application_controller.rb9
-rw-r--r--tool/ruby_vm/helpers/c_escape.rb8
-rw-r--r--tool/ruby_vm/helpers/dumper.rb20
-rw-r--r--tool/ruby_vm/helpers/scanner.rb9
-rw-r--r--tool/ruby_vm/loaders/insns_def.rb46
-rw-r--r--tool/ruby_vm/loaders/opt_insn_unif_def.rb2
-rw-r--r--tool/ruby_vm/loaders/opt_operand_def.rb2
-rw-r--r--tool/ruby_vm/loaders/vm_opts_h.rb2
-rw-r--r--tool/ruby_vm/models/attribute.rb21
-rwxr-xr-xtool/ruby_vm/models/bare_instructions.rb63
-rw-r--r--tool/ruby_vm/models/c_expr.rb2
-rw-r--r--tool/ruby_vm/models/instructions.rb2
-rw-r--r--tool/ruby_vm/models/instructions_unifications.rb2
-rw-r--r--tool/ruby_vm/models/operands_unifications.rb11
-rw-r--r--tool/ruby_vm/models/trace_instructions.rb2
-rw-r--r--tool/ruby_vm/models/typemap.rb9
-rw-r--r--tool/ruby_vm/scripts/insns2vm.rb14
-rw-r--r--tool/ruby_vm/views/_attributes.erb2
-rw-r--r--tool/ruby_vm/views/_c_expr.erb2
-rw-r--r--tool/ruby_vm/views/_comptime_insn_stack_increase.erb62
-rw-r--r--tool/ruby_vm/views/_copyright.erb20
-rw-r--r--tool/ruby_vm/views/_insn_entry.erb56
-rw-r--r--tool/ruby_vm/views/_insn_len_info.erb13
-rw-r--r--tool/ruby_vm/views/_insn_name_info.erb40
-rw-r--r--tool/ruby_vm/views/_insn_operand_info.erb36
-rw-r--r--tool/ruby_vm/views/_insn_sp_pc_dependency.erb27
-rw-r--r--tool/ruby_vm/views/_insn_stack_increase.erb52
-rw-r--r--tool/ruby_vm/views/_insn_type_chars.erb26
-rw-r--r--tool/ruby_vm/views/_leaf_helpers.erb67
-rw-r--r--tool/ruby_vm/views/_mjit_compile_insn.erb86
-rw-r--r--tool/ruby_vm/views/_mjit_compile_insn_body.erb105
-rw-r--r--tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb36
-rw-r--r--tool/ruby_vm/views/_mjit_compile_send.erb89
-rw-r--r--tool/ruby_vm/views/_mjit_compile_send_guard.erb14
-rw-r--r--tool/ruby_vm/views/_notice.erb2
-rw-r--r--tool/ruby_vm/views/_sp_inc_helpers.erb37
-rw-r--r--tool/ruby_vm/views/_trace_instruction.erb11
-rw-r--r--tool/ruby_vm/views/insns.inc.erb2
-rw-r--r--tool/ruby_vm/views/insns_info.inc.erb6
-rw-r--r--tool/ruby_vm/views/lib/ruby_vm/mjit/instruction.rb.erb40
-rw-r--r--tool/ruby_vm/views/mjit_compile.inc.erb75
-rw-r--r--tool/ruby_vm/views/mjit_sp_inc.inc.erb17
-rw-r--r--tool/ruby_vm/views/opt_sc.inc.erb2
-rw-r--r--tool/ruby_vm/views/optinsn.inc.erb6
-rw-r--r--tool/ruby_vm/views/optunifs.inc.erb2
-rw-r--r--tool/ruby_vm/views/vm.inc.erb2
-rw-r--r--tool/ruby_vm/views/vmtc.inc.erb2
-rwxr-xr-xtool/runruby.rb51
-rw-r--r--tool/search-cgvars.rb55
-rwxr-xr-xtool/strip-rdoc.rb30
-rwxr-xr-x[-rw-r--r--]tool/sync_default_gems.rb959
-rwxr-xr-xtool/test-annocheck.sh33
-rw-r--r--tool/test-bundled-gems.rb109
-rw-r--r--tool/test-coverage.rb2
-rw-r--r--tool/test/runner.rb23
-rw-r--r--tool/test/test_jisx0208.rb2
-rwxr-xr-xtool/test/test_sync_default_gems.rb76
-rw-r--r--tool/test/testunit/metametameta.rb70
-rw-r--r--tool/test/testunit/test4test_hideskip.rb14
-rw-r--r--tool/test/testunit/test4test_redefinition.rb14
-rw-r--r--tool/test/testunit/test4test_sorting.rb18
-rw-r--r--tool/test/testunit/test_assertion.rb53
-rw-r--r--tool/test/testunit/test_hideskip.rb21
-rw-r--r--tool/test/testunit/test_minitest_unit.rb1488
-rw-r--r--tool/test/testunit/test_parallel.rb219
-rw-r--r--tool/test/testunit/test_redefinition.rb11
-rw-r--r--tool/test/testunit/test_sorting.rb75
-rw-r--r--tool/test/testunit/tests_for_parallel/ptest_first.rb (renamed from test/testunit/tests_for_parallel/ptest_first.rb)0
-rw-r--r--tool/test/testunit/tests_for_parallel/ptest_forth.rb (renamed from test/testunit/tests_for_parallel/ptest_forth.rb)0
-rw-r--r--tool/test/testunit/tests_for_parallel/ptest_second.rb (renamed from test/testunit/tests_for_parallel/ptest_second.rb)0
-rw-r--r--tool/test/testunit/tests_for_parallel/ptest_third.rb (renamed from test/testunit/tests_for_parallel/ptest_third.rb)0
-rw-r--r--tool/test/testunit/tests_for_parallel/runner.rb14
-rw-r--r--tool/test/testunit/tests_for_parallel/test4test_hungup.rb15
-rw-r--r--tool/test/webrick/.htaccess (renamed from test/webrick/.htaccess)0
-rw-r--r--tool/test/webrick/test_cgi.rb170
-rw-r--r--tool/test/webrick/test_config.rb (renamed from test/webrick/test_config.rb)0
-rw-r--r--tool/test/webrick/test_cookie.rb (renamed from test/webrick/test_cookie.rb)0
-rw-r--r--tool/test/webrick/test_do_not_reverse_lookup.rb (renamed from test/webrick/test_do_not_reverse_lookup.rb)0
-rw-r--r--tool/test/webrick/test_filehandler.rb403
-rw-r--r--tool/test/webrick/test_htgroup.rb (renamed from test/webrick/test_htgroup.rb)0
-rw-r--r--tool/test/webrick/test_htmlutils.rb (renamed from test/webrick/test_htmlutils.rb)0
-rw-r--r--tool/test/webrick/test_httpauth.rb366
-rw-r--r--tool/test/webrick/test_httpproxy.rb467
-rw-r--r--tool/test/webrick/test_httprequest.rb488
-rw-r--r--tool/test/webrick/test_httpresponse.rb282
-rw-r--r--tool/test/webrick/test_https.rb (renamed from test/webrick/test_https.rb)0
-rw-r--r--tool/test/webrick/test_httpserver.rb543
-rw-r--r--tool/test/webrick/test_httpstatus.rb35
-rw-r--r--tool/test/webrick/test_httputils.rb (renamed from test/webrick/test_httputils.rb)0
-rw-r--r--tool/test/webrick/test_httpversion.rb (renamed from test/webrick/test_httpversion.rb)0
-rw-r--r--tool/test/webrick/test_server.rb191
-rw-r--r--tool/test/webrick/test_ssl_server.rb67
-rw-r--r--tool/test/webrick/test_utils.rb110
-rw-r--r--tool/test/webrick/utils.rb84
-rw-r--r--tool/test/webrick/webrick.cgi (renamed from test/webrick/webrick.cgi)0
-rw-r--r--tool/test/webrick/webrick.rhtml (renamed from test/webrick/webrick.rhtml)0
-rw-r--r--tool/test/webrick/webrick_long_filename.cgi (renamed from test/webrick/webrick_long_filename.cgi)0
-rw-r--r--tool/transcode-tblgen.rb30
-rw-r--r--tool/transform_mjit_header.rb92
-rwxr-xr-xtool/travis_retry.sh13
-rwxr-xr-xtool/travis_wait.sh18
-rw-r--r--tool/update-NEWS-refs.rb37
-rwxr-xr-xtool/update-bundled_gems.rb23
-rwxr-xr-xtool/update-deps49
-rw-r--r--tool/vcs.rb528
-rw-r--r--tool/vpath.rb87
-rwxr-xr-xtool/wasm-clangw9
-rwxr-xr-xtool/ytab.sed4
-rw-r--r--trace_point.rb420
-rw-r--r--transcode.c873
-rw-r--r--transcode_data.h37
-rw-r--r--transient_heap.c991
-rw-r--r--transient_heap.h65
-rw-r--r--util.c3642
-rw-r--r--variable.c3832
-rw-r--r--variable.h29
-rw-r--r--version.c89
-rw-r--r--version.h72
-rw-r--r--vm.c3218
-rw-r--r--vm_args.c960
-rw-r--r--vm_backtrace.c1198
-rw-r--r--vm_callinfo.h550
-rw-r--r--vm_core.h1236
-rw-r--r--vm_debug.h99
-rw-r--r--vm_dump.c1305
-rw-r--r--vm_eval.c1794
-rw-r--r--vm_exec.c80
-rw-r--r--vm_exec.h69
-rw-r--r--vm_insnhelper.c6392
-rw-r--r--vm_insnhelper.h182
-rw-r--r--vm_method.c2455
-rw-r--r--vm_opts.h35
-rw-r--r--vm_sync.c301
-rw-r--r--vm_sync.h137
-rw-r--r--vm_trace.c1523
-rw-r--r--vsnprintf.c30
-rw-r--r--warning.rb53
-rw-r--r--wasm/GNUmakefile.in32
-rw-r--r--wasm/README.md70
-rw-r--r--wasm/asyncify.h23
-rw-r--r--wasm/fiber.c83
-rw-r--r--wasm/fiber.h43
-rw-r--r--wasm/machine.c62
-rw-r--r--wasm/machine.h25
-rw-r--r--wasm/machine_core.S25
-rw-r--r--wasm/missing.c199
-rw-r--r--wasm/runtime.c47
-rw-r--r--wasm/setjmp.c204
-rw-r--r--wasm/setjmp.h95
-rw-r--r--wasm/setjmp_core.S27
-rw-r--r--wasm/tests/fiber_test.c66
-rw-r--r--wasm/tests/machine_test.c115
-rw-r--r--wasm/tests/setjmp_test.c108
-rwxr-xr-xwasm/wasm-opt36
-rw-r--r--win32/Makefile.sub375
-rw-r--r--win32/README.win32101
-rwxr-xr-xwin32/configure.bat42
-rw-r--r--win32/dir.h9
-rw-r--r--win32/file.c636
-rw-r--r--win32/file.h38
-rwxr-xr-xwin32/ifchange.bat31
-rwxr-xr-xwin32/mkexports.rb18
-rwxr-xr-xwin32/resource.rb4
-rwxr-xr-xwin32/rm.bat2
-rw-r--r--win32/ruby.manifest8
-rw-r--r--win32/setup.mak139
-rw-r--r--win32/win32.c6372
-rw-r--r--win32/winmain.c4
-rw-r--r--yjit.c1109
-rw-r--r--yjit.h70
-rw-r--r--yjit.rb356
-rw-r--r--yjit/.gitignore2
-rw-r--r--yjit/Cargo.lock49
-rw-r--r--yjit/Cargo.toml47
-rw-r--r--yjit/bindgen/Cargo.lock311
-rw-r--r--yjit/bindgen/Cargo.toml10
-rw-r--r--yjit/bindgen/src/main.rs430
-rw-r--r--yjit/not_gmake.mk14
-rw-r--r--yjit/src/asm/arm64/README.md16
-rw-r--r--yjit/src/asm/arm64/arg/bitmask_imm.rs255
-rw-r--r--yjit/src/asm/arm64/arg/condition.rs52
-rw-r--r--yjit/src/asm/arm64/arg/inst_offset.rs47
-rw-r--r--yjit/src/asm/arm64/arg/mod.rs18
-rw-r--r--yjit/src/asm/arm64/arg/sf.rs19
-rw-r--r--yjit/src/asm/arm64/arg/shifted_imm.rs81
-rw-r--r--yjit/src/asm/arm64/arg/sys_reg.rs6
-rw-r--r--yjit/src/asm/arm64/arg/truncate.rs66
-rw-r--r--yjit/src/asm/arm64/inst/atomic.rs86
-rw-r--r--yjit/src/asm/arm64/inst/branch.rs100
-rw-r--r--yjit/src/asm/arm64/inst/branch_cond.rs78
-rw-r--r--yjit/src/asm/arm64/inst/breakpoint.rs55
-rw-r--r--yjit/src/asm/arm64/inst/call.rs104
-rw-r--r--yjit/src/asm/arm64/inst/conditional.rs73
-rw-r--r--yjit/src/asm/arm64/inst/data_imm.rs143
-rw-r--r--yjit/src/asm/arm64/inst/data_reg.rs192
-rw-r--r--yjit/src/asm/arm64/inst/halfword_imm.rs179
-rw-r--r--yjit/src/asm/arm64/inst/load_literal.rs89
-rw-r--r--yjit/src/asm/arm64/inst/load_register.rs108
-rw-r--r--yjit/src/asm/arm64/inst/load_store.rs249
-rw-r--r--yjit/src/asm/arm64/inst/load_store_exclusive.rs109
-rw-r--r--yjit/src/asm/arm64/inst/logical_imm.rs154
-rw-r--r--yjit/src/asm/arm64/inst/logical_reg.rs207
-rw-r--r--yjit/src/asm/arm64/inst/mod.rs50
-rw-r--r--yjit/src/asm/arm64/inst/mov.rs155
-rw-r--r--yjit/src/asm/arm64/inst/nop.rs44
-rw-r--r--yjit/src/asm/arm64/inst/pc_rel.rs107
-rw-r--r--yjit/src/asm/arm64/inst/reg_pair.rs212
-rw-r--r--yjit/src/asm/arm64/inst/sbfm.rs103
-rw-r--r--yjit/src/asm/arm64/inst/shift_imm.rs147
-rw-r--r--yjit/src/asm/arm64/inst/sys_reg.rs86
-rw-r--r--yjit/src/asm/arm64/inst/test_bit.rs133
-rw-r--r--yjit/src/asm/arm64/mod.rs1580
-rw-r--r--yjit/src/asm/arm64/opnd.rs195
-rw-r--r--yjit/src/asm/mod.rs792
-rw-r--r--yjit/src/asm/x86_64/mod.rs1415
-rw-r--r--yjit/src/asm/x86_64/tests.rs449
-rw-r--r--yjit/src/backend/arm64/mod.rs1491
-rw-r--r--yjit/src/backend/ir.rs1576
-rw-r--r--yjit/src/backend/mod.rs8
-rw-r--r--yjit/src/backend/tests.rs331
-rw-r--r--yjit/src/backend/x86_64/mod.rs895
-rw-r--r--yjit/src/codegen.rs7721
-rw-r--r--yjit/src/core.rs2400
-rw-r--r--yjit/src/cruby.rs715
-rw-r--r--yjit/src/cruby_bindings.inc.rs1310
-rw-r--r--yjit/src/disasm.rs269
-rw-r--r--yjit/src/invariants.rs567
-rw-r--r--yjit/src/lib.rs18
-rw-r--r--yjit/src/options.rs174
-rw-r--r--yjit/src/stats.rs640
-rw-r--r--yjit/src/utils.rs274
-rw-r--r--yjit/src/virtualmem.rs443
-rw-r--r--yjit/src/yjit.rs136
-rw-r--r--yjit/yjit.mk69
7917 files changed, 1250863 insertions, 405170 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000000..05ff204541
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,134 @@
+---
+version: '{build}'
+init:
+ - git config --global user.name git
+ - git config --global user.email svn-admin@ruby-lang.org
+ - git config --global core.autocrlf false
+ - git config --global core.eol lf
+ - git config --global advice.detachedHead 0
+shallow_clone: true
+clone_depth: 10
+platform:
+ - x64
+skip_commits:
+ message: /\[DOC\]/
+ files:
+ - doc/*
+ - '**/*.md'
+ - '**/*.rdoc'
+ - '**/.document'
+ - '**/*.[1-8]'
+ - '**/*.ronn'
+environment:
+ ruby_version: "24-%Platform%"
+ matrix:
+ # Test only the oldest supported version because AppVeyor is unstable, its concurrency
+ # is limited, and compatibility issues that happen only in newer versions are rare.
+ # You may test some other stuff on GitHub Actions instead.
+ - build: vs
+ vs: 120 # Visual Studio 2013
+ ssl: OpenSSL-v111
+ # The worker image name. This is NOT the Visual Studio version we're using here.
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ GEMS_FOR_TEST: ""
+ RELINE_TEST_ENCODING: "UTF-8"
+cache:
+ - c:\Tools\vcpkg\installed\
+for:
+-
+ matrix:
+ only:
+ - build: vs
+ install:
+ - ver
+ - chcp
+ - SET BITS=%Platform:x86=32%
+ - SET BITS=%BITS:x=%
+ - SET OPENSSL_DIR=C:\%ssl%-Win%BITS%
+ - cd C:\Tools\vcpkg
+ - git pull -q
+ - .\bootstrap-vcpkg.bat
+ - ps: Start-FileDownload 'https://github.com/microsoft/vcpkg-tool/releases/download/2023-08-09/vcpkg.exe' -FileName 'C:\Tools\vcpkg\vcpkg.exe'
+ - cd %APPVEYOR_BUILD_FOLDER%
+ - vcpkg --triplet %Platform%-windows install --x-use-aria2 libffi libyaml readline zlib
+ - CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat
+ - SET vcvars
+ - '"%vcvars%" %Platform:x64=amd64%'
+ - SET ruby_path=C:\Ruby%ruby_version:-x86=%
+ - SET PATH=\usr\local\bin;%ruby_path%\bin;%PATH%;C:\msys64\mingw64\bin;C:\msys64\usr\bin
+ - ruby --version
+ - 'cl'
+ - echo> Makefile srcdir=.
+ - echo>> Makefile MSC_VER=0
+ - echo>> Makefile RT=none
+ - echo>> Makefile RT_VER=0
+ - echo>> Makefile BUILTIN_ENCOBJS=nul
+ - type win32\Makefile.sub >> Makefile
+ - nmake %mflags% up VCSUP="echo Update OK"
+ - nmake %mflags% extract-extlibs
+ - del Makefile
+ - mkdir \usr\local\bin
+ - mkdir \usr\local\include
+ - mkdir \usr\local\lib
+ - for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
+ - for %%I in (c:\Tools\vcpkg\installed\%Platform%-windows\bin\*.dll) do (
+ if not %%~nI == readline mklink \usr\local\bin\%%~nxI %%I
+ )
+ - attrib +r /s /d
+ - mkdir %Platform%-mswin_%vs%
+ build_script:
+ - set HAVE_GIT=no
+ - cd %APPVEYOR_BUILD_FOLDER%
+ - cd %Platform%-mswin_%vs%
+ - >-
+ ..\win32\configure.bat
+ --with-opt-dir="/usr/local;c:/Tools/vcpkg/installed/%Platform%-windows"
+ --with-openssl-dir=%OPENSSL_DIR:\=/%
+ - nmake -l
+ - nmake install-nodoc
+ - \usr\bin\ruby -v -e "p :locale => Encoding.find('locale'), :filesystem => Encoding.find('filesystem')"
+ - if not "%GEMS_FOR_TEST%" == "" \usr\bin\gem install --no-document %GEMS_FOR_TEST%
+ - \usr\bin\ruby -ropenssl -e "puts 'Build ' + OpenSSL::OPENSSL_VERSION, 'Runtime ' + OpenSSL::OPENSSL_LIBRARY_VERSION"
+ test_script:
+ - set /a JOBS=%NUMBER_OF_PROCESSORS%
+ - nmake -l "TESTOPTS=-v -q" btest
+ - nmake -l "TESTOPTS=-v -q" test-basic
+ - >-
+ nmake -l "TESTOPTS=--timeout-scale=3.0
+ --excludes=../test/excludes/_appveyor -j%JOBS%
+ --exclude win32ole
+ --exclude test_bignum
+ --exclude test_syntax
+ --exclude test_open-uri
+ --exclude test_bundled_ca
+ " test-all
+ # separately execute tests without -j which may crash worker with -j.
+ - >-
+ nmake -l
+ "TESTOPTS=--timeout-scale=3.0 --excludes=../test/excludes/_appveyor"
+ TESTS="
+ ../test/win32ole
+ ../test/ruby/test_bignum.rb
+ ../test/ruby/test_syntax.rb
+ ../test/open-uri/test_open-uri.rb
+ ../test/rubygems/test_bundled_ca.rb
+ " test-all
+ - nmake -l test-spec MSPECOPT=-fs # not using `-j` because sometimes `mspec -j` silently dies on Windows
+notifications:
+ - provider: Webhook
+ method: POST
+ url:
+ secure: CcFlJNDJ/a6to7u3Z4Fnz6dScEPNx7hTha2GkSRlV+1U6dqmxY/7uBcLXYb9gR3jfQk6w+2o/HrjNAyXMNGU/JOka3s2WRI4VKitzM+lQ08owvJIh0R7LxrGH0J2e81U # ruby-lang slack: ruby/simpler-alerts-bot
+ body: >-
+ {{^isPullRequest}}
+ {
+ "ci": "AppVeyor CI",
+ "env": "Visual Studio 2013",
+ "url": "{{buildUrl}}",
+ "commit": "{{commitId}}",
+ "branch": "{{branch}}"
+ }
+ {{/isPullRequest}}
+ on_build_success: false
+ on_build_failure: true
+ on_build_status_changed: false
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 0000000000..8f4b96445c
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,39 @@
+;; Copyright (c) 2018 Urabe, Shyouhei. All rights reserved.
+;;
+;; This file is a part of the programming language Ruby. Permission is hereby
+;; granted, to either redistribute and/or modify this file, provided that the
+;; conditions mentioned in the file COPYING are met. Consult the file for
+;; details.
+
+((nil .
+ ((indent-tabs-mode . nil)
+ (require-final-newline . t)
+ (tab-width . 8)
+ (show-trailing-whitespace . t)
+ (whitespace-line-column . 80))) ;; See also [Misc #12277]
+
+ ;; (bat-mode . ((buffer-file-coding-system . utf-8-dos)))
+
+ (ruby-mode . ((ruby-indent-level . 2)))
+
+ (rdoc-mode . ((fill-column . 74)))
+
+ (yaml-mode . ((yaml-indent-offset . 2)))
+
+ (makefile-mode . ((indent-tabs-mode . t)))
+
+ (c-mode . ((c-file-style . "ruby")))
+
+ (c++-mode . ((c-file-style . "ruby")))
+
+ (change-log-mode .
+ ((buffer-file-coding-system . us-ascii)
+ (indent-tabs-mode . t)
+ (change-log-indent-text . 2)
+ (add-log-time-format . (lambda (&optional x y)
+ (let* ((time (or x (current-time)))
+ (system-time-locale "C")
+ (diff (+ (cadr time) 32400))
+ (lo (% diff 65536))
+ (hi (+ (car time) (/ diff 65536))))
+ (format-time-string "%a %b %e %H:%M:%S %Y" (list hi lo) t)))))))
diff --git a/.document b/.document
index 0cc939508e..3a6b0c238c 100644
--- a/.document
+++ b/.document
@@ -9,9 +9,28 @@
# prelude
prelude.rb
-
rbconfig.rb
+array.rb
+ast.rb
+dir.rb
+gc.rb
+io.rb
+kernel.rb
+marshal.rb
+mjit.rb
+numeric.rb
+nilclass.rb
+pack.rb
+ractor.rb
+string.rb
+symbol.rb
+timev.rb
+thread_sync.rb
+trace_point.rb
+warning.rb
+yjit.rb
+
# the lib/ directory (which has its own .document file)
lib
@@ -19,14 +38,15 @@ lib
ext
# rdoc files
-NEWS
+NEWS.md
README.md
README.ja.md
COPYING
COPYING.ja
-CONTRIBUTING.md
+
+LEGAL
# win32/README.win32 linked from README.md
win32
diff --git a/.editorconfig b/.editorconfig
index b7ff7280ad..4633a7acae 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -22,3 +22,9 @@ indent_size = 2
[{*[Mm]akefile*,*.mak,*.mk,depend}]
indent_style = tab
+
+[enc/*]
+indent_size = 2
+
+[reg*.[ch]]
+indent_size = 2
diff --git a/.gdbinit b/.gdbinit
index 63cd706476..34d044caf6 100644
--- a/.gdbinit
+++ b/.gdbinit
@@ -136,7 +136,7 @@ define rp
printf "%sT_ARRAY%s: len=%ld ", $color_type, $color_end, $len
if ($flags & RUBY_FL_USER2)
printf "(shared) shared="
- output/x ((struct RArray*)($arg0))->as.heap.aux.shared
+ output/x ((struct RArray*)($arg0))->as.heap.aux.shared_root
printf " "
else
printf "(ownership) capa=%ld ", ((struct RArray*)($arg0))->as.heap.aux.capa
@@ -156,8 +156,12 @@ define rp
else
if ($flags & RUBY_T_MASK) == RUBY_T_HASH
printf "%sT_HASH%s: ", $color_type, $color_end,
- if ((struct RHash *)($arg0))->ntbl
- printf "len=%ld ", ((struct RHash *)($arg0))->ntbl->num_entries
+ if (((struct RHash *)($arg0))->basic.flags & RHASH_ST_TABLE_FLAG)
+ printf "st len=%ld ", ((struct RHash *)($arg0))->as.st->num_entries
+ else
+ printf "li len=%ld bound=%ld ", \
+ ((((struct RHash *)($arg0))->basic.flags & RHASH_AR_TABLE_SIZE_MASK) >> RHASH_AR_TABLE_SIZE_SHIFT), \
+ ((((struct RHash *)($arg0))->basic.flags & RHASH_AR_TABLE_BOUND_MASK) >> RHASH_AR_TABLE_BOUND_SHIFT)
end
print (struct RHash *)($arg0)
else
@@ -261,6 +265,10 @@ define rp
printf "%sT_ZOMBIE%s: ", $color_type, $color_end
print (struct RData *)($arg0)
else
+ if ($flags & RUBY_T_MASK) == RUBY_T_MOVED
+ printf "%sT_MOVED%s: ", $color_type, $color_end
+ print *(struct RMoved *)$arg0
+ else
printf "%sunknown%s: ", $color_type, $color_end
print (struct RBasic *)($arg0)
end
@@ -296,6 +304,7 @@ define rp
end
end
end
+ end
end
document rp
Print a Ruby's VALUE.
@@ -535,13 +544,13 @@ end
define rp_class
printf "(struct RClass *) %p", (void*)$arg0
- if ((struct RClass *)($arg0))->ptr.origin_ != $arg0
- printf " -> %p", ((struct RClass *)($arg0))->ptr.origin_
+ if RCLASS_ORIGIN((struct RClass *)($arg0)) != $arg0
+ printf " -> %p", RCLASS_ORIGIN((struct RClass *)($arg0))
end
printf "\n"
rb_classname $arg0
print/x *(struct RClass *)($arg0)
- print *((struct RClass *)($arg0))->ptr
+ print *RCLASS_EXT((struct RClass *)($arg0))
end
document rp_class
Print the content of a Class/Module.
@@ -970,8 +979,8 @@ end
define rb_ps_vm
print $ps_vm = (rb_vm_t*)$arg0
- set $ps_thread_ln = $ps_vm->living_threads.n.next
- set $ps_thread_ln_last = $ps_vm->living_threads.n.prev
+ set $ps_thread_ln = $ps_vm->ractor.main_ractor.threads.set.n.next
+ set $ps_thread_ln_last = $ps_vm->ractor.main_ractor.threads.set.n.prev
while 1
set $ps_thread_th = (rb_thread_t *)$ps_thread_ln
set $ps_thread = (VALUE)($ps_thread_th->self)
@@ -1069,9 +1078,9 @@ define print_id
else
set $serial = (rb_id_serial_t)$id
end
- if $serial && $serial <= global_symbols.last_id
+ if $serial && $serial <= ruby_global_symbols.last_id
set $idx = $serial / ID_ENTRY_UNIT
- set $ids = (struct RArray *)global_symbols.ids
+ set $ids = (struct RArray *)ruby_global_symbols.ids
set $flags = $ids->basic.flags
if ($flags & RUBY_FL_USER1)
set $idsptr = $ids->as.ary
@@ -1093,11 +1102,11 @@ define print_id
set $arylen = $ary->as.heap.len
end
set $result = $aryptr[($serial % ID_ENTRY_UNIT) * ID_ENTRY_SIZE + $t]
- if $result != RUBY_Qnil
+ if $result != RUBY_Qnil
print_string $result
- else
- echo undef
- end
+ else
+ echo undef
+ end
end
end
end
@@ -1270,7 +1279,7 @@ document rb_count_objects
Counts all objects grouped by type.
end
-# Details: https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/MachineInstructionsTraceWithGDB
+# Details: https://bugs.ruby-lang.org/projects/ruby-master/wiki/MachineInstructionsTraceWithGDB
define trace_machine_instructions
set logging on
set height 0
@@ -1315,8 +1324,7 @@ define print_flags
printf "RUBY_FL_PROMOTED0 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_PROMOTED0 ? "1" : "0"
printf "RUBY_FL_PROMOTED1 : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_PROMOTED1 ? "1" : "0"
printf "RUBY_FL_FINALIZE : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_FINALIZE ? "1" : "0"
- printf "RUBY_FL_TAINT : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_TAINT ? "1" : "0"
- printf "RUBY_FL_UNTRUSTED : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_UNTRUSTED ? "1" : "0"
+ printf "RUBY_FL_SHAREABLE : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_SHAREABLE ? "1" : "0"
printf "RUBY_FL_EXIVAR : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_EXIVAR ? "1" : "0"
printf "RUBY_FL_FREEZE : %s\n", ((struct RBasic*)($arg0))->flags & RUBY_FL_FREEZE ? "1" : "0"
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000000..6c5eac5a0f
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,23 @@
+# This is a file used by GitHub to ignore the following commits on `git blame`.
+#
+# You can also do the same thing in your local repository with:
+# $ git config --local blame.ignoreRevsFile .git-blame-ignore-revs
+
+# Expand tabs
+5b21e94bebed90180d8ff63dad03b8b948361089
+
+# Enable Style/StringLiterals cop for RubyGems/Bundler
+d7ffd3fea402239b16833cc434404a7af82d44f3
+
+# [ruby/digest] Revert tab-expansion in external files
+48b09aae7ec5632209229dcc294dd0d75a93a17f
+8a65cf3b61c60e4cb886f59a73ff6db44364bfa9
+39dc9f9093901d40d2998653948d5da38b18ee2c
+
+# [ruby/io-nonblock] Revert tab expansion
+f28287d34c03f472ffe90ea262bdde9affd4b965
+0d842fecb4f75ab3b1d4097ebdb8e88f51558041
+4ba2c66761d6a293abdfba409241d31063cefd62
+
+# Make benchmark indentation consistent
+fc4acf8cae82e5196186d3278d831f2438479d91
diff --git a/.gitattributes b/.gitattributes
index 6ca2f89462..d0c2d266b4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,3 +4,5 @@ bin svn-properties=svn:ignore=ruby
bin/* diff=ruby
tool/update-deps diff=ruby
tool/make-snapshot diff=ruby
+tool/format-release diff=ruby
+tool/leaked-globals diff=ruby
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 0000000000..56baa29942
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,9 @@
+# Security Policy
+
+## Supported Versions
+
+See <https://www.ruby-lang.org/en/downloads/branches/>.
+
+## Reporting a Vulnerability
+
+See <https://www.ruby-lang.org/en/security/>.
diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml
new file mode 100644
index 0000000000..91f82b842b
--- /dev/null
+++ b/.github/codeql/codeql-config.yml
@@ -0,0 +1,3 @@
+name: "CodeQL config for the Ruby language"
+
+languages: cpp
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..bc63aca35b
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: 'github-actions'
+ directory: '/'
+ schedule:
+ interval: 'monthly'
diff --git a/.github/workflows/baseruby.yml b/.github/workflows/baseruby.yml
new file mode 100644
index 0000000000..ebaafe3bf0
--- /dev/null
+++ b/.github/workflows/baseruby.yml
@@ -0,0 +1,80 @@
+name: BASERUBY Check
+
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ baseruby:
+ name: BASERUBY
+ runs-on: ubuntu-22.04
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ strategy:
+ matrix:
+ ruby:
+ - ruby-2.2
+# - ruby-2.3
+# - ruby-2.4
+# - ruby-2.5
+# - ruby-2.6
+# - ruby-2.7
+ - ruby-3.0
+ - ruby-3.1
+
+ steps:
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: .downloaded-cache
+ key: downloaded-cache
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: ${{ matrix.ruby }}
+ bundler: none
+ - run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - run: sudo apt-get install build-essential autoconf bison libyaml-dev
+ - run: ./autogen.sh
+ - run: ./configure --disable-install-doc
+ - run: make common-srcs
+ - run: make incs
+ - run: make all
+ - run: make test
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / BASERUBY @ ${{ matrix.ruby }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
diff --git a/.github/workflows/bundled_gems.yml b/.github/workflows/bundled_gems.yml
new file mode 100644
index 0000000000..070c0fa1dd
--- /dev/null
+++ b/.github/workflows/bundled_gems.yml
@@ -0,0 +1,166 @@
+name: bundled_gems
+
+on:
+ push:
+ branches: [ "master" ]
+ paths:
+ - '.github/workflows/bundled_gems.yml'
+ - 'gems/bundled_gems'
+ pull_request:
+ branches: [ "master" ]
+ paths:
+ - '.github/workflows/bundled_gems.yml'
+ - 'gems/bundled_gems'
+ merge_group:
+ branches: [ "master" ]
+ paths:
+ - '.github/workflows/bundled_gems.yml'
+ - 'gems/bundled_gems'
+ schedule:
+ - cron: '45 6 * * *'
+ workflow_dispatch:
+
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
+
+jobs:
+ update:
+ permissions:
+ contents: write # for Git to git push
+ if: ${{ github.event_name != 'schedule' || github.repository == 'ruby/ruby' }}
+ name: update ${{ github.workflow }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+
+ - name: Set ENV
+ run: |
+ echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ echo "TODAY=$(date +%F)" >> $GITHUB_ENV
+
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: .downloaded-cache
+ key: downloaded-cache-${{ github.sha }}
+ restore-keys: |
+ downloaded-cache
+
+ - name: Download previous gems list
+ run: |
+ data=bundled_gems.json
+ mkdir -p .downloaded-cache
+ ln -s .downloaded-cache/$data .
+ curl -O -R -z ./$data https://stdgems.org/$data
+
+ - name: Update bundled gems list
+ run: |
+ ruby -i~ tool/update-bundled_gems.rb gems/bundled_gems
+
+ - name: Maintain updated gems list in NEWS
+ run: |
+ #!ruby
+ require 'json'
+ news = File.read("NEWS.md")
+ prev = news[/since the \*+(\d+\.\d+\.\d+)\*+/, 1]
+ prevs = [prev, prev.sub(/\.\d+\z/, '')]
+ %W[bundled].each do |type|
+ last = JSON.parse(File.read("#{type}_gems.json"))['gems'].filter_map do |g|
+ v = g['versions'].values_at(*prevs).compact.first
+ g = g['gem']
+ g = 'RubyGems' if g == 'rubygems'
+ [g, v] if v
+ end.to_h
+ changed = File.foreach("gems/#{type}_gems").filter_map do |l|
+ next if l.start_with?("#")
+ g, v = l.split(" ", 3)
+ [g, v] unless last[g] == v
+ end
+ changed, added = changed.partition {|g, _| last[g]}
+ news.sub!(/^\*( +)The following #{type} gems? are updated\.\n+\K(?: \1\*( +).*\n)*/) do
+ mark = "#{$1} *#{$2}"
+ changed.map {|g, v|"#{mark}#{g} #{v}\n"}.join("")
+ end or next
+ news.sub!(/^\*( +)The following default gems are now bundled gems\.\n+\K(?: \1\*( +).*\n)*/) do
+ mark = "#{$1} *#{$2}"
+ added.map {|g, v|"#{mark}#{g} #{v}\n"}.join("")
+ end or next unless added.empty?
+ File.write("NEWS.md", news)
+ end
+ shell: ruby {0}
+
+ - name: Check diffs
+ id: diff
+ run: |
+ git add -- NEWS.md
+ git diff --no-ext-diff --ignore-submodules --quiet -- gems/bundled_gems
+ continue-on-error: true
+
+ - name: Install libraries
+ run: |
+ set -x
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby
+ if: ${{ steps.diff.outcome == 'failure' }}
+
+ - name: Build
+ run: |
+ ./autogen.sh
+ ./configure -C --disable-install-doc
+ make
+ if: ${{ steps.diff.outcome == 'failure' }}
+
+ - name: Prepare bundled gems
+ run: |
+ make -s prepare-gems
+ if: ${{ steps.diff.outcome == 'failure' }}
+
+ - name: Test bundled gems
+ run: |
+ make -s test-bundled-gems
+ git add -- gems/bundled_gems
+ timeout-minutes: 30
+ env:
+ RUBY_TESTOPTS: "-q --tty=no"
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
+ if: ${{ steps.diff.outcome == 'failure' }}
+
+ - name: Show diffs
+ id: show
+ run: |
+ git diff --cached --color --no-ext-diff --ignore-submodules --exit-code --
+ continue-on-error: true
+
+ - name: Commit
+ run: |
+ git pull --ff-only origin ${GITHUB_REF#refs/heads/}
+ message="Update bundled gems list at "
+ if [ ${{ steps.diff.outcome }} = success ]; then
+ git commit --message="${message}${GITHUB_SHA:0:30} [ci skip]"
+ else
+ git commit --message="${message}${TODAY}"
+ fi
+ git push origin ${GITHUB_REF#refs/heads/}
+ env:
+ EMAIL: svn-admin@ruby-lang.org
+ GIT_AUTHOR_NAME: git
+ GIT_COMMITTER_NAME: git
+ if: ${{ github.repository == 'ruby/ruby' && !startsWith(github.event_name, 'pull') && steps.show.outcome == 'failure' }}
+
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / update",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml
new file mode 100644
index 0000000000..79b2916feb
--- /dev/null
+++ b/.github/workflows/check_dependencies.yml
@@ -0,0 +1,78 @@
+name: Check Dependencies
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ update-deps:
+ strategy:
+ matrix:
+ os: [ubuntu-22.04]
+ fail-fast: true
+ runs-on: ${{ matrix.os }}
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - name: Install libraries
+ run: |
+ set -x
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby
+ if: ${{ contains(matrix.os, 'ubuntu') }}
+ - name: Install libraries
+ run: |
+ brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline
+ if: ${{ contains(matrix.os, 'macos') }}
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: .downloaded-cache
+ key: downloaded-cache
+ - run: ./autogen.sh
+ - name: Run configure
+ run: ./configure -C --disable-install-doc --disable-rubygems --with-gcc 'optflags=-O0' 'debugflags=-save-temps=obj -g'
+ - run: make all golf
+ - run: ruby tool/update-deps --fix
+ - run: git diff --no-ext-diff --ignore-submodules --exit-code
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ matrix.os }} / Dependencies need to update",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000000..8dba76fbe2
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,75 @@
+name: "Code scanning - action"
+
+on:
+ # push:
+ # paths-ignore:
+ # - 'doc/**'
+ # - '**/man'
+ # - '**.md'
+ # - '**.rdoc'
+ # - '**/.document'
+ # pull_request:
+ # paths-ignore:
+ # - 'doc/**'
+ # - '**/man'
+ # - '**.md'
+ # - '**.rdoc'
+ # - '**/.document'
+ schedule:
+ - cron: '0 12 * * *'
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
+
+jobs:
+ CodeQL-Build:
+
+ # CodeQL runs on ubuntu-latest and windows-latest
+ permissions:
+ actions: read # for github/codeql-action/init to get workflow details
+ contents: read # for actions/checkout to fetch code
+ security-events: write # for github/codeql-action/autobuild to send a status report
+ runs-on: ubuntu-latest
+ # CodeQL fails to run pull requests from dependabot due to missing write access to upload results.
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') && github.event.head_commit.pusher.name != 'dependabot[bot]' }}
+
+ env:
+ enable_install_doc: no
+
+ steps:
+ - name: Install libraries
+ run: |
+ set -x
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby
+
+ - name: Checkout repository
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: .downloaded-cache
+ key: downloaded-cache
+
+ - name: Remove an obsolete rubygems vendored file
+ run: sudo rm /usr/lib/ruby/vendor_ruby/rubygems/defaults/operating_system.rb
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
+ with:
+ config-file: ./.github/codeql/codeql-config.yml
+ trap-caching: false
+
+ - name: Set ENV
+ run: echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.37
diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml
new file mode 100644
index 0000000000..caf12cc0f4
--- /dev/null
+++ b/.github/workflows/compilers.yml
@@ -0,0 +1,281 @@
+name: Compilations
+
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+# GitHub actions does not support YAML anchors. This creative use of
+# environment variables (plus the "echo $GITHUB_ENV" hack) is to reroute that
+# restriction.
+env:
+ default_cc: clang-15
+ append_cc: ''
+
+ # -O1 is faster than -O3 in our tests... Majority of time are consumed trying
+ # to optimize binaries. Also GitHub Actions run on relatively modern CPUs
+ # compared to, say, GCC 4 or Clang 3. We don't specify `-march=native`
+ # because compilers tend not understand what the CPU is.
+ optflags: '-O1'
+
+ # -g0 disables backtraces when SEGV. Do not set that.
+ debugflags: '-ggdb3'
+
+ default_configure: >-
+ --enable-debug-env
+ --disable-install-doc
+ --with-ext=-test-/cxxanyargs,+
+ append_configure: >-
+ --without-valgrind
+ --without-jemalloc
+ --without-gmp
+
+ UPDATE_UNICODE: >-
+ UNICODE_FILES=.
+ UNICODE_PROPERTY_FILES=.
+ UNICODE_AUXILIARY_FILES=.
+ UNICODE_EMOJI_FILES=.
+ CONFIGURE_TTY: never
+ GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ RUBY_DEBUG: ci rgengc
+ RUBY_TESTOPTS: >-
+ -q
+ --color=always
+ --tty=no
+
+permissions:
+ contents: read
+
+jobs:
+ compile:
+ strategy:
+ fail-fast: false
+ matrix:
+ env:
+ - {}
+ entry:
+ - { name: gcc-12, env: { default_cc: gcc-12 } }
+ - { name: gcc-11, env: { default_cc: gcc-11 } }
+ - { name: gcc-10, env: { default_cc: gcc-10 } }
+ - { name: gcc-9, env: { default_cc: gcc-9 } }
+ - { name: gcc-8, env: { default_cc: gcc-8 } }
+ - { name: gcc-7, env: { default_cc: gcc-7 } }
+ - name: 'gcc-13 LTO'
+ container: gcc-13
+ env:
+ default_cc: 'gcc-13 -flto=auto -ffat-lto-objects -Werror=lto-type-mismatch'
+ optflags: '-O2'
+ shared: disable
+ # check: true
+ - { name: clang-16, env: { default_cc: clang-16 } }
+ - { name: clang-15, env: { default_cc: clang-15 } }
+ - { name: clang-14, env: { default_cc: clang-14 } }
+ - { name: clang-13, env: { default_cc: clang-13 } }
+ - { name: clang-12, env: { default_cc: clang-12 } }
+ - { name: clang-11, env: { default_cc: clang-11 } }
+ - { name: clang-10, env: { default_cc: clang-10 } }
+ # llvm-objcopy<=9 doesn't have --wildcard. It compiles, but leaves Rust symbols in libyjit.o.
+ - { name: clang-9, env: { default_cc: clang-9, append_configure: '--disable-yjit' } }
+ - { name: clang-8, env: { default_cc: clang-8, append_configure: '--disable-yjit' } }
+ - { name: clang-7, env: { default_cc: clang-7, append_configure: '--disable-yjit' } }
+ - { name: clang-6.0, env: { default_cc: clang-6.0, append_configure: '--disable-yjit' } }
+ - name: 'clang-16 LTO'
+ container: clang-16
+ env:
+ default_cc: 'clang-16 -flto=auto'
+ optflags: '-O2'
+ shared: disable
+ # check: true
+
+# - { name: aarch64-linux-gnu, crosshost: aarch64-linux-gnu, container: crossbuild-essential-arm64 }
+# - { name: arm-linux-gnueabi, crosshost: arm-linux-gnueabi }
+# - { name: arm-linux-gnueabihf, crosshost: arm-linux-gnueabihf }
+# - { name: i686-w64-mingw32, crosshost: i686-w64-mingw32 }
+# - { name: powerpc-linux-gnu, crosshost: powerpc-linux-gnu }
+# - { name: powerpc64le-linux-gnu, crosshost: powerpc64le-linux-gnu, container: crossbuild-essential-ppc64el }
+# - { name: s390x-linux-gnu, crosshost: s390x-linux-gnu, container: crossbuild-essential-s390x }
+# - { name: x86_64-w64-mingw32, crosshost: x86_64-w64-mingw32, container: mingw-w64 }
+
+ # -Wno-strict-prototypes is necessary with current clang-15 since
+ # older autoconf generate functions without prototype and -pedantic
+ # now implies strict-prototypes. Disabling the error but leaving the
+ # warning generates a lot of noise from use of ANYARGS in
+ # rb_define_method() and friends.
+ # See: https://github.com/llvm/llvm-project/commit/11da1b53d8cd3507959022cd790d5a7ad4573d94
+ - { name: c99, env: { append_cc: '-std=c99 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
+# - { name: c11, env: { append_cc: '-std=c11 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
+# - { name: c17, env: { append_cc: '-std=c17 -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
+ - { name: c2x, env: { append_cc: '-std=c2x -Werror=pedantic -pedantic-errors -Wno-strict-prototypes' } }
+ - { name: c++98, env: { CXXFLAGS: '-std=c++98 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
+# - { name: c++11, env: { CXXFLAGS: '-std=c++11 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
+# - { name: c++14, env: { CXXFLAGS: '-std=c++14 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
+# - { name: c++17, env: { CXXFLAGS: '-std=c++17 -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
+ - { name: c++2a, env: { CXXFLAGS: '-std=c++2a -Werror=pedantic -pedantic-errors -Wno-c++11-long-long' } }
+
+ - { name: '-O0', env: { optflags: '-O0 -march=x86-64 -mtune=generic' } }
+# - { name: '-O3', env: { optflags: '-O3 -march=x86-64 -mtune=generic' }, check: true }
+
+ - { name: gmp, env: { append_configure: '--with-gmp' } }
+ - { name: jemalloc, env: { append_configure: '--with-jemalloc' } }
+ - { name: valgrind, env: { append_configure: '--with-valgrind' } }
+ - { name: 'coroutine=ucontext', env: { append_configure: '--with-coroutine=ucontext' } }
+ - { name: 'coroutine=pthread', env: { append_configure: '--with-coroutine=pthread' } }
+ - { name: disable-jit-support, env: { append_configure: '--disable-jit-support' } }
+ - { name: disable-dln, env: { append_configure: '--disable-dln' } }
+ - { name: enable-mkmf-verbose, env: { append_configure: '--enable-mkmf-verbose' } }
+ - { name: disable-rubygems, env: { append_configure: '--disable-rubygems' } }
+ - { name: RUBY_DEVEL, env: { append_configure: '--enable-devel' } }
+
+ - { name: OPT_THREADED_CODE=1, env: { cppflags: '-DOPT_THREADED_CODE=1' } }
+ - { name: OPT_THREADED_CODE=2, env: { cppflags: '-DOPT_THREADED_CODE=2' } }
+ - { name: OPT_THREADED_CODE=3, env: { cppflags: '-DOPT_THREADED_CODE=3' } }
+
+ - { name: NDEBUG, env: { cppflags: '-DNDEBUG' } }
+ - { name: RUBY_DEBUG, env: { cppflags: '-DRUBY_DEBUG' } }
+# - { name: ARRAY_DEBUG, env: { cppflags: '-DARRAY_DEBUG' } }
+# - { name: BIGNUM_DEBUG, env: { cppflags: '-DBIGNUM_DEBUG' } }
+# - { name: CCAN_LIST_DEBUG, env: { cppflags: '-DCCAN_LIST_DEBUG' } }
+# - { name: CPDEBUG=-1, env: { cppflags: '-DCPDEBUG=-1' } }
+# - { name: ENC_DEBUG, env: { cppflags: '-DENC_DEBUG' } }
+# - { name: GC_DEBUG, env: { cppflags: '-DGC_DEBUG' } }
+# - { name: HASH_DEBUG, env: { cppflags: '-DHASH_DEBUG' } }
+# - { name: ID_TABLE_DEBUG, env: { cppflags: '-DID_TABLE_DEBUG' } }
+# - { name: RGENGC_DEBUG=-1, env: { cppflags: '-DRGENGC_DEBUG=-1' } }
+# - { name: SYMBOL_DEBUG, env: { cppflags: '-DSYMBOL_DEBUG' } }
+
+# - { name: RGENGC_CHECK_MODE, env: { cppflags: '-DRGENGC_CHECK_MODE' } }
+# - { name: TRANSIENT_HEAP_CHECK_MODE, env: { cppflags: '-DTRANSIENT_HEAP_CHECK_MODE' } }
+# - { name: VM_CHECK_MODE, env: { cppflags: '-DVM_CHECK_MODE' } }
+
+ - { name: USE_EMBED_CI=0, env: { cppflags: '-DUSE_EMBED_CI=0' } }
+ - name: USE_FLONUM=0,
+ env:
+ cppflags: '-DUSE_FLONUM=0'
+ # yjit requires FLONUM for the pointer tagging scheme
+ append_configure: '--disable-yjit'
+# - { name: USE_GC_MALLOC_OBJ_INFO_DETAILS, env: { cppflags: '-DUSE_GC_MALLOC_OBJ_INFO_DETAILS' } }
+ - { name: USE_LAZY_LOAD, env: { cppflags: '-DUSE_LAZY_LOAD' } }
+# - { name: USE_RINCGC=0, env: { cppflags: '-DUSE_RINCGC=0' } }
+# - { name: USE_SYMBOL_GC=0, env: { cppflags: '-DUSE_SYMBOL_GC=0' } }
+# - { name: USE_THREAD_CACHE=0, env: { cppflags: '-DUSE_THREAD_CACHE=0' } }
+# - { name: USE_TRANSIENT_HEAP=0, env: { cppflags: '-DUSE_TRANSIENT_HEAP=0' } }
+# - { name: USE_RUBY_DEBUG_LOG=1, env: { cppflags: '-DUSE_RUBY_DEBUG_LOG=1' } }
+ - { name: USE_RVARGC=0, env: { cppflags: '-DUSE_RVARGC=0' } }
+# - { name: USE_RVARGC=1, env: { cppflags: '-DUSE_RVARGC=1' } }
+# - { name: USE_DEBUG_COUNTER, env: { cppflags: '-DUSE_DEBUG_COUNTER=1', RUBY_DEBUG_COUNTER_DISABLE: '1' } }
+
+ - { name: DEBUG_FIND_TIME_NUMGUESS, env: { cppflags: '-DDEBUG_FIND_TIME_NUMGUESS' } }
+ - { name: DEBUG_INTEGER_PACK, env: { cppflags: '-DDEBUG_INTEGER_PACK' } }
+# - { name: ENABLE_PATH_CHECK, env: { cppflags: '-DENABLE_PATH_CHECK' } }
+
+ - { name: GC_DEBUG_STRESS_TO_CLASS, env: { cppflags: '-DGC_DEBUG_STRESS_TO_CLASS' } }
+# - { name: GC_ENABLE_LAZY_SWEEP=0, env: { cppflags: '-DGC_ENABLE_LAZY_SWEEP=0' } }
+# - { name: GC_PROFILE_DETAIL_MEMOTY, env: { cppflags: '-DGC_PROFILE_DETAIL_MEMOTY' } }
+# - { name: GC_PROFILE_MORE_DETAIL, env: { cppflags: '-DGC_PROFILE_MORE_DETAIL' } }
+
+# - { name: CALC_EXACT_MALLOC_SIZE, env: { cppflags: '-DCALC_EXACT_MALLOC_SIZE' } }
+# - { name: MALLOC_ALLOCATED_SIZE_CHECK, env: { cppflags: '-DMALLOC_ALLOCATED_SIZE_CHECK' } }
+
+# - { name: IBF_ISEQ_ENABLE_LOCAL_BUFFER, env: { cppflags: '-DIBF_ISEQ_ENABLE_LOCAL_BUFFER' } }
+
+# - { name: RGENGC_ESTIMATE_OLDMALLOC, env: { cppflags: '-DRGENGC_ESTIMATE_OLDMALLOC' } }
+# - { name: RGENGC_FORCE_MAJOR_GC, env: { cppflags: '-DRGENGC_FORCE_MAJOR_GC' } }
+# - { name: RGENGC_OBJ_INFO, env: { cppflags: '-DRGENGC_OBJ_INFO' } }
+# - { name: RGENGC_OLD_NEWOBJ_CHECK, env: { cppflags: '-DRGENGC_OLD_NEWOBJ_CHECK' } }
+# - { name: RGENGC_PROFILE, env: { cppflags: '-DRGENGC_PROFILE' } }
+
+# - { name: VM_DEBUG_BP_CHECK, env: { cppflags: '-DVM_DEBUG_BP_CHECK' } }
+# - { name: VM_DEBUG_VERIFY_METHOD_CACHE, env: { cppflags: '-DVM_DEBUG_VERIFY_METHOD_CACHE' } }
+
+ - { name: MJIT_FORCE_ENABLE, env: { cppflags: '-DMJIT_FORCE_ENABLE' } }
+ - { name: YJIT_FORCE_ENABLE, env: { cppflags: '-DYJIT_FORCE_ENABLE' } }
+
+ name: ${{ matrix.entry.name }}
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/ruby/ruby-ci-image:${{ matrix.entry.container || matrix.entry.env.default_cc || 'clang-15' }}
+ options: --user root
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ env: ${{ matrix.entry.env || matrix.env }}
+ steps:
+ - run: id
+ working-directory:
+ - run: mkdir build
+ working-directory:
+ - name: setenv
+ run: |
+ echo "GNUMAKEFLAGS=-sj$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - run: ./autogen.sh
+ working-directory: src
+ - name: Run configure
+ run: >
+ ../src/configure -C ${default_configure} ${append_configure}
+ --${{
+ matrix.entry.crosshost && 'host' || 'with-gcc'
+ }}=${{
+ matrix.entry.crosshost || '"${default_cc}${append_cc:+ $append_cc}"'
+ }}
+ --${{ matrix.entry.shared || 'enable' }}-shared
+ - run: make extract-extlibs
+ - run: make incs
+ - run: make showflags
+ - run: make
+ - run: make leaked-globals
+ - run: make test
+ - run: make install
+ if: ${{ matrix.entry.check }}
+ - run: make test-tool
+ if: ${{ matrix.entry.check }}
+ - run: make test-all TESTS='-- ruby -ext-'
+ if: ${{ matrix.entry.check }}
+ - run: make test-spec
+ env:
+ CHECK_LEAKS: true
+ if: ${{ matrix.entry.check }}
+ - run: make test-annocheck
+ if: ${{ matrix.entry.check && endsWith(matrix.entry.name, 'annocheck') }}
+
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / ${{ matrix.entry.name }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
new file mode 100644
index 0000000000..d8dc58b119
--- /dev/null
+++ b/.github/workflows/macos.yml
@@ -0,0 +1,113 @@
+name: macOS
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ test_task: ["check"] # "test-bundler-parallel", "test-bundled-gems"
+ os:
+ - macos-13
+ - macos-14
+ - macos-15
+ fail-fast: false
+ env:
+ GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ runs-on: ${{ matrix.os }}
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: Install libraries
+ run: |
+ brew install gmp libffi openssl@1.1 zlib autoconf automake libtool readline bison
+ working-directory: src
+ - name: Set ENV
+ run: |
+ echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV
+ echo "PATH="/usr/local/opt/bison/bin:/opt/homebrew/opt/bison/bin:$PATH"" >> $GITHUB_ENV
+ - run: ./autogen.sh
+ working-directory: src
+ - name: Run configure
+ run: ../src/configure -C --disable-install-doc --with-openssl-dir=$(brew --prefix openssl@1.1) --with-readline-dir=$(brew --prefix readline)
+ - run: make incs
+ - run: make prepare-gems
+ if: ${{ matrix.test_task == 'test-bundled-gems' }}
+ - run: make
+ - run: make leaked-globals
+ if: ${{ matrix.test_task == 'check' }}
+ - name: make ${{ matrix.test_task }}
+ run: |
+ make -s ${{ matrix.test_task }} ${TESTS:+TESTS=`echo "$TESTS" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|'`}
+ timeout-minutes: 40
+ env:
+ RUBY_TESTOPTS: "-q --tty=no"
+ TESTS: ${{ matrix.test_task == 'check' && matrix.skipped_tests || '' }}
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
+ PRECHECK_BUNDLED_GEMS: "no"
+ - name: make skipped tests
+ run: |
+ make -s test-all TESTS=`echo "$TESTS" | sed 's| |$$/ -n/|g;s|^|-n/|;s|$|$$/|'`
+ env:
+ GNUMAKEFLAGS: ""
+ RUBY_TESTOPTS: "-v --tty=no"
+ TESTS: ${{ matrix.skipped_tests }}
+ PRECHECK_BUNDLED_GEMS: "no"
+ if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }}
+ continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml
new file mode 100644
index 0000000000..0df917d3d8
--- /dev/null
+++ b/.github/workflows/mingw.yml
@@ -0,0 +1,179 @@
+name: MinGW
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+# Notes:
+# Actions console encoding causes issues, see test-all & test-spec steps
+#
+jobs:
+ make:
+ runs-on: windows-2022
+ name: ${{ github.workflow }} (${{ matrix.msystem }})
+ env:
+ MSYSTEM: ${{ matrix.msystem }}
+ MSYS2_ARCH: x86_64
+ CHOST: "x86_64-w64-mingw32"
+ CFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe"
+ CXXFLAGS: "-march=x86-64 -mtune=generic -O3 -pipe"
+ CPPFLAGS: "-D_FORTIFY_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 -DFD_SETSIZE=2048"
+ LDFLAGS: "-pipe"
+ UPDATE_UNICODE: "UNICODE_FILES=. UNICODE_PROPERTY_FILES=. UNICODE_AUXILIARY_FILES=. UNICODE_EMOJI_FILES=."
+ GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ strategy:
+ matrix:
+ include:
+ # To mitigate flakiness of MinGW CI, we test only one runtime that newer MSYS2 uses.
+ - msystem: "UCRT64"
+ base_ruby: head
+ test_task: "check"
+ test-all-opts: "--name=!/TestObjSpace#test_reachable_objects_during_iteration/"
+ fail-fast: false
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: git config
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.eol lf
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: Set up Ruby & MSYS2
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: ${{ matrix.base_ruby }}
+ - name: set env
+ run: |
+ echo "GNUMAKEFLAGS=-j$((2 * NUMBER_OF_PROCESSORS))" >> $GITHUB_ENV
+
+ - name: where check
+ run: |
+ # show where
+ mv /c/Windows/System32/libcrypto-1_1-x64.dll /c/Windows/System32/libcrypto-1_1-x64.dll_
+ mv /c/Windows/System32/libssl-1_1-x64.dll /c/Windows/System32/libssl-1_1-x64.dll_
+ result=true
+ for e in gcc.exe ragel.exe make.exe bison.exe libcrypto-1_1-x64.dll libssl-1_1-x64.dll; do
+ echo '##['group']'$'\033[93m'$e$'\033[m'
+ where $e || result=false
+ echo '##['endgroup']'
+ done
+ $result
+
+ - name: version check
+ run: |
+ # show version
+ result=true
+ for e in gcc ragel make bison "openssl version"; do
+ case "$e" in *" "*) ;; *) e="$e --version";; esac
+ echo '##['group']'$'\033[93m'$e$'\033[m'
+ $e || result=false
+ echo '##['endgroup']'
+ done
+ $result
+
+ - name: autogen
+ run: |
+ ./autogen.sh
+ working-directory: src
+
+ - name: configure
+ run: >
+ ../src/configure --disable-install-doc --prefix=/.
+ --build=$CHOST --host=$CHOST --target=$CHOST
+
+ - name: update
+ run: |
+ make incs
+
+ - name: download gems
+ run: |
+ make update-gems
+
+ - name: make all
+ timeout-minutes: 30
+ run: |
+ make
+
+ - run: make leaked-globals
+
+ - name: make install
+ run: |
+ make DESTDIR=../install install-nodoc
+
+ - name: test
+ timeout-minutes: 5
+ run: |
+ make test
+ if: ${{matrix.test_task == 'check' || matrix.test_task == 'test'}}
+
+ - name: test-all
+ timeout-minutes: 45
+ run: |
+ # Actions uses UTF8, causes test failures, similar to normal OS setup
+ chcp.com 437
+ make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }}
+ env:
+ RUBY_TESTOPTS: >-
+ --retry --job-status=normal --show-skip --timeout-scale=1.5
+ ${{ matrix.test-all-opts }}
+ BUNDLER_VERSION:
+ if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-all' || StartsWith(matrix.test_task, 'test/')}}
+
+ - name: test-spec
+ timeout-minutes: 10
+ run: |
+ make ${{ StartsWith(matrix.test_task, 'spec/') && matrix.test_task || 'test-spec' }}
+ if: ${{matrix.test_task == 'check' || matrix.test_task == 'test-spec' || StartsWith(matrix.test_task, 'spec/')}}
+
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} ${{ matrix.msystem }} / ${{ matrix.test_task }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
+ shell: sh
diff --git a/.github/workflows/mjit-bindgen.yml b/.github/workflows/mjit-bindgen.yml
new file mode 100644
index 0000000000..26f8a1b2aa
--- /dev/null
+++ b/.github/workflows/mjit-bindgen.yml
@@ -0,0 +1,104 @@
+name: MJIT bindgen
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ include:
+ - task: mjit-bindgen
+ fail-fast: false
+ runs-on: ubuntu-22.04
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: Set ENV
+ run: |
+ echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - name: Install libraries
+ run: |
+ set -x
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y \
+ build-essential \
+ libssl-dev libyaml-dev libreadline6-dev \
+ zlib1g-dev libncurses5-dev libffi-dev \
+ libclang1-14 \
+ bison autoconf
+ sudo apt-get install -q -y pkg-config || :
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: '3.1'
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: Fixed world writable dirs
+ run: |
+ chmod -v go-w $HOME $HOME/.config
+ sudo chmod -R go-w /usr/share
+ sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
+ - run: ./autogen.sh
+ working-directory: src
+ - name: Run configure
+ run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug
+ - run: make incs
+ - run: make
+ - run: make install
+ - run: make ${{ matrix.task }}
+ - run: git diff --exit-code
+ working-directory: src
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ matrix.os }} / ${{ matrix.test_task }}${{ matrix.configure }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/mjit.yml b/.github/workflows/mjit.yml
new file mode 100644
index 0000000000..6f7181489a
--- /dev/null
+++ b/.github/workflows/mjit.yml
@@ -0,0 +1,113 @@
+name: MJIT
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ - '**.[1-8]'
+ - '**.ronn'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ - '**.[1-8]'
+ - '**.ronn'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ test_task: [check] # to make job names consistent
+ mjit_opts: [--mjit-wait]
+ fail-fast: false
+ runs-on: ubuntu-latest
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ env:
+ TESTOPTS: '-q --tty=no'
+ RUN_OPTS: '--disable-gems ${{ matrix.mjit_opts }} --mjit-debug=-ggdb3'
+ GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: Install libraries
+ run: |
+ set -x
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: Fixed world writable dirs
+ run: |
+ chmod -v go-w $HOME $HOME/.config
+ sudo chmod -R go-w /usr/share
+ sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
+ - name: Set ENV
+ run: |
+ echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - run: ./autogen.sh
+ working-directory: src
+ - name: Run configure
+ run: ../src/configure -C --disable-install-doc cppflags=-DVM_CHECK_MODE
+ - run: make incs
+ - run: make
+ - run: sudo make -s install
+ - name: Run test
+ run: |
+ unset GNUMAKEFLAGS
+ make -s test RUN_OPTS="$RUN_OPTS"
+ timeout-minutes: 60
+ # - name: Run test-all
+ # run: |
+ # ulimit -c unlimited
+ # make -s test-all RUN_OPTS="$RUN_OPTS"
+ # timeout-minutes: 60
+ - name: Run test-spec
+ run: |
+ unset GNUMAKEFLAGS
+ make -s test-spec RUN_OPTS="$RUN_OPTS"
+ timeout-minutes: 60
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.mjit_opts }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000000..5d4474d978
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,18 @@
+name: Start release workflow
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+ notify:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build release package
+ run: |
+ curl -L -X POST \
+ -H "Authorization: Bearer ${{ secrets.MATZBOT_GITHUB_WORKFLOW_TOKEN }}" \
+ -H "Accept: application/vnd.github+json" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
+ https://api.github.com/repos/ruby/actions/dispatches \
+ -d '{"event_type": "${{ github.ref }}"}'
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
new file mode 100644
index 0000000000..c12a95362d
--- /dev/null
+++ b/.github/workflows/scorecards.yml
@@ -0,0 +1,72 @@
+# This workflow uses actions that are not certified by GitHub. They are provided
+# by a third-party and are governed by separate terms of service, privacy
+# policy, and support documentation.
+
+name: Scorecards supply-chain security
+on:
+ # For Branch-Protection check. Only the default branch is supported. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+ branch_protection_rule:
+ # To guarantee Maintained check is occasionally updated. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+ schedule:
+ - cron: '22 4 * * 2'
+ push:
+ branches: [ "master" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecards analysis
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Needed to publish results and get a badge (see publish_results below).
+ id-token: write
+ # Uncomment the permissions below if installing in a private repository.
+ # contents: read
+ # actions: read
+
+ steps:
+ - name: "Checkout code"
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@ea651e62978af7915d09fe2e282747c798bf2dab # v2.4.1
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) Read-only PAT token. Uncomment the `repo_token` line below if:
+ # - you want to enable the Branch-Protection check on a *public* repository, or
+ # - you are installing Scorecards on a *private* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
+ repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
+
+ # Public repositories:
+ # - Publish results to OpenSSF REST API for easy access by consumers
+ # - Allows the repository to include the Scorecard badge.
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
+ # For private repositories:
+ # - `publish_results` will always be set to `false`, regardless
+ # of the value entered here.
+ publish_results: true
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard.
+ - name: "Upload to code-scanning"
+ uses: github/codeql-action/upload-sarif@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # v2.1.27
+ with:
+ sarif_file: results.sarif
diff --git a/.github/workflows/spec_guards.yml b/.github/workflows/spec_guards.yml
new file mode 100644
index 0000000000..4521195a2b
--- /dev/null
+++ b/.github/workflows/spec_guards.yml
@@ -0,0 +1,71 @@
+name: Rubyspec Version Guards Check
+
+on:
+ push:
+ paths:
+ - 'spec/**'
+ - '!spec/*.md'
+ pull_request:
+ paths:
+ - 'spec/**'
+ - '!spec/*.md'
+ merge_group:
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ rubyspec:
+ name: Rubyspec
+
+ runs-on: ubuntu-22.04
+
+ if: >-
+ ${{!(false
+ || contains(github.event.head_commit.message, '[DOC]')
+ || contains(github.event.head_commit.message, 'Document')
+ || contains(github.event.pull_request.title, '[DOC]')
+ || contains(github.event.pull_request.title, 'Document')
+ || contains(github.event.pull_request.labels.*.name, 'Document')
+ || (github.event_name == 'push' && github.actor == 'dependabot[bot]')
+ )}}
+
+ strategy:
+ matrix:
+ # Specs from ruby/spec should still run on all supported Ruby versions.
+ # This also ensures the needed ruby_version_is guards are there, see spec/README.md.
+ ruby:
+ - ruby-3.1
+ - ruby-3.2
+
+ steps:
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: ${{ matrix.ruby }}
+ bundler: none
+
+ - run: gem install webrick
+
+ - run: ruby ../mspec/bin/mspec
+ working-directory: spec/ruby
+ env:
+ CHECK_LEAKS: true
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / rubyspec @ ${{ matrix.ruby }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() }}
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
new file mode 100644
index 0000000000..4fbca1170e
--- /dev/null
+++ b/.github/workflows/ubuntu.yml
@@ -0,0 +1,146 @@
+name: Ubuntu
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ # main variables included in the job name
+ test_task: [check]
+ configure: [cppflags=-DRUBY_DEBUG] # default to use more assertions
+ arch: ['']
+ # specify all jobs with `include` to avoid testing duplicated things
+ include:
+ - test_task: check
+ - test_task: check
+ arch: i686
+ configure: '' # test without -DRUBY_DEBUG as well
+ - test_task: check
+ configure: "--enable-shared --enable-load-relative"
+ - test_task: test-all TESTS=--repeat-count=2
+ - test_task: test-bundler-parallel
+ - test_task: test-bundled-gems
+ fail-fast: false
+ env:
+ GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ RUBY_DEBUG: ci
+ SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }}
+ runs-on: ubuntu-22.04
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: Set ENV
+ run: |
+ echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - name: Install libraries
+ env:
+ arch: ${{matrix.arch}}
+ run: |
+ set -x
+ arch=${arch:+:${arch/i[3-6]86/i386}}
+ ${arch:+sudo dpkg --add-architecture ${arch#:}}
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y \
+ ${arch:+cross}build-essential${arch/:/-} \
+ libssl-dev${arch} libyaml-dev${arch} libreadline6-dev${arch} \
+ zlib1g-dev${arch} libncurses5-dev${arch} libffi-dev${arch} \
+ bison autoconf ruby
+ sudo apt-get install -q -y pkg-config${arch} || :
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: Fixed world writable dirs
+ run: |
+ chmod -v go-w $HOME $HOME/.config
+ sudo chmod -R go-w /usr/share
+ sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
+ - run: ./autogen.sh
+ working-directory: src
+ - name: Run configure
+ env:
+ arch: ${{matrix.arch}}
+ run: >-
+ $SETARCH ../src/configure -C --disable-install-doc ${{ matrix.configure }}
+ ${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE}
+ - run: $SETARCH make incs
+ - run: $SETARCH make prepare-gems
+ if: ${{ matrix.test_task == 'test-bundled-gems' }}
+ - run: $SETARCH make
+ - run: $SETARCH make leaked-globals
+ if: ${{ matrix.test_task == 'check' }}
+ - name: Create dummy files in build dir
+ run: |
+ $SETARCH ./miniruby -e '(("a".."z").to_a+("A".."Z").to_a+("0".."9").to_a+%w[foo bar test zzz]).each{|basename|File.write("#{basename}.rb", "raise %(do not load #{basename}.rb)")}'
+ if: ${{ matrix.test_task == 'check' }}
+ - name: make ${{ matrix.test_task }}
+ run: |
+ $SETARCH make -s ${{ matrix.test_task }} ${TESTS:+TESTS=`echo "$TESTS" | sed 's| |$$/ -n!/|g;s|^|-n!/|;s|$|$$/|'`}
+ timeout-minutes: 40
+ env:
+ RUBY_TESTOPTS: "-q --tty=no"
+ TESTS: ${{ matrix.test_task == 'check' && matrix.skipped_tests || '' }}
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
+ PRECHECK_BUNDLED_GEMS: "no"
+ - name: make skipped tests
+ run: |
+ $SETARCH make -s test-all TESTS=`echo "$TESTS" | sed 's| |$$/ -n/|g;s|^|-n/|;s|$|$$/|'`
+ env:
+ GNUMAKEFLAGS: ""
+ RUBY_TESTOPTS: "-v --tty=no"
+ TESTS: ${{ matrix.skipped_tests }}
+ if: ${{ matrix.test_task == 'check' && matrix.skipped_tests != '' }}
+ continue-on-error: ${{ matrix.continue-on-skipped_tests || false }}
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}${{ matrix.arch }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
new file mode 100644
index 0000000000..27920b5821
--- /dev/null
+++ b/.github/workflows/wasm.yml
@@ -0,0 +1,146 @@
+name: WebAssembly
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions: # added using https://github.com/step-security/secure-workflows
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ entry:
+# # wasmtime can't compile non-optimized Asyncified binary due to locals explosion
+# - { name: O0-debuginfo, optflags: "-O0", debugflags: "-g", wasmoptflags: "-O1" }
+# - { name: O1, optflags: "-O1", debugflags: "" , wasmoptflags: "-O1" }
+ - { name: O2, optflags: "-O2", debugflags: "" , wasmoptflags: "-O2" }
+# - { name: O3, optflags: "-O3", debugflags: "" , wasmoptflags: "-O3" }
+# # -O4 is equivalent to -O3 in clang, but it's different in wasm-opt
+# - { name: O4, optflags: "-O3", debugflags: "" , wasmoptflags: "-O4" }
+# - { name: Oz, optflags: "-Oz", debugflags: "" , wasmoptflags: "-Oz" }
+ fail-fast: false
+ env:
+ RUBY_TESTOPTS: '-q --tty=no'
+ GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ WASI_SDK_VERSION_MAJOR: 14
+ WASI_SDK_VERSION_MINOR: 0
+ BINARYEN_VERSION: 109
+ WASMTIME_VERSION: v0.33.0
+ runs-on: ubuntu-22.04
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - name: Install libraries
+ run: |
+ set -ex
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y ruby bison make autoconf git wget
+
+ wasi_sdk_deb="wasi-sdk_${WASI_SDK_VERSION_MAJOR}.${WASI_SDK_VERSION_MINOR}_amd64.deb"
+ wget "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION_MAJOR}/${wasi_sdk_deb}"
+ sudo dpkg -i "$wasi_sdk_deb"
+ rm -f "$wasi_sdk_deb"
+
+ mkdir build-sdk
+ pushd build-sdk
+
+ wasmtime_url="https://github.com/bytecodealliance/wasmtime/releases/download/${WASMTIME_VERSION}/wasmtime-${WASMTIME_VERSION}-x86_64-linux.tar.xz"
+ wget -O - "$wasmtime_url" | tar xJf -
+ sudo ln -fs "$PWD/wasmtime-${WASMTIME_VERSION}-x86_64-linux/wasmtime" /usr/local/bin/wasmtime
+
+ binaryen_tarball="binaryen-version_${BINARYEN_VERSION}-x86_64-linux.tar.gz"
+ binaryen_url="https://github.com/WebAssembly/binaryen/releases/download/version_${BINARYEN_VERSION}/${binaryen_tarball}"
+ wget -O - "$binaryen_url" | tar xfz -
+ sudo ln -fs "$PWD/binaryen-version_${BINARYEN_VERSION}/bin/wasm-opt" /usr/local/bin/wasm-opt
+ working-directory: src
+ - name: Set ENV
+ run: |
+ echo "MAKEFLAGS=-j$((1 + $(sysctl -n hw.activecpu)))" >> $GITHUB_ENV
+ echo "WASI_SDK_PATH=/opt/wasi-sdk" >> $GITHUB_ENV
+ - run: ./autogen.sh
+ working-directory: src
+
+ - uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
+ with:
+ ruby-version: '3.0'
+ bundler: none
+
+ - name: Download config.guess with wasi version
+ run: |
+ rm tool/config.guess tool/config.sub
+ ruby tool/downloader.rb -d tool -e gnu config.guess config.sub
+ working-directory: src
+
+ - name: Run configure
+ run: |
+ ../src/configure \
+ --host wasm32-unknown-wasi \
+ --with-static-linked-ext \
+ LDFLAGS=" \
+ -Xlinker --stack-first \
+ -Xlinker -z -Xlinker stack-size=16777216 \
+ " \
+ optflags="${{ matrix.entry.optflags }}" \
+ debugflags="${{ matrix.entry.debugflags }}" \
+ wasmoptflags="${{ matrix.entry.wasmoptflags }} ${{ matrix.entry.debugflags }}"
+
+ # miniruby may not be built when cross-compling
+ - run: make mini ruby
+ - name: Run basictest
+ run: wasmtime run ./../build/miniruby --mapdir /::./ -- basictest/test.rb
+ working-directory: src
+ - name: Run bootstraptest (no thread)
+ run: |
+ NO_THREAD_TESTS="$(grep -L Thread -R ./bootstraptest | awk -F/ '{ print $NF }' | uniq | sed -n 's/test_\(.*\).rb/\1/p' | paste -s -d, -)"
+ ruby ./bootstraptest/runner.rb --ruby="$(which wasmtime) run $PWD/../build/ruby --mapdir /::./ -- " --verbose "--sets=$NO_THREAD_TESTS"
+ working-directory: src
+
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / ${{ matrix.name }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
new file mode 100644
index 0000000000..c2bd4881c2
--- /dev/null
+++ b/.github/workflows/windows.yml
@@ -0,0 +1,149 @@
+name: Windows
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ make:
+ strategy:
+ matrix:
+ include:
+ - vs: 2022
+ vcvers: -vcvars_ver=14.2
+ fail-fast: false
+ runs-on: windows-${{ matrix.vs }}
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ name: VisualStudio ${{ matrix.vs }}
+ env:
+ GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ PATCH: C:\msys64\usr\bin\patch.exe
+ OS_VER: windows-${{ matrix.vs }}
+ steps:
+ - run: md build
+ working-directory:
+ - uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2.27.0
+ id: setup-msys2
+ with:
+ update: true
+ install: bison patch
+ - name: patch path
+ shell: msys2 {0}
+ run: echo PATCH=$(cygpath -wa $(command -v patch)) >> $GITHUB_ENV
+ if: ${{ steps.setup-msys2.outcome == 'success' }}
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: C:\vcpkg\installed
+ key: ${{ runner.os }}-vcpkg-installed-windows-${{ matrix.vs }}-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-vcpkg-installed-windows-${{ matrix.vs }}-
+ ${{ runner.os }}-vcpkg-installed-windows-
+ - name: Install libraries with vcpkg
+ run: |
+ iex "& {$(irm get.scoop.sh)} -RunAsAdmin"
+ Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
+ scoop install cmake@3.31.6
+ vcpkg --triplet x64-windows install libffi libyaml openssl readline zlib
+ shell:
+ pwsh
+ - name: git config
+ run: |
+ git config --global core.autocrlf false
+ git config --global core.eol lf
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: setup env
+ # %TEMP% is inconsistent with %TMP% and test-all expects they are consistent.
+ # https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302
+ # msys2/setup-msys2 installs MSYS2 to D:/a/_temp/msys64/usr/bin
+ run: |
+ set Path=D:/a/_temp/msys64/usr/bin;%Path%
+ if not "%VCVARS%" == "" goto :vcset
+ set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\${{ matrix.vs }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
+ :vcset
+ set | C:\msys64\usr\bin\sort > old.env
+ call %VCVARS% ${{ matrix.vcvers || ''}}
+ set TMP=%USERPROFILE%\AppData\Local\Temp
+ set TEMP=%USERPROFILE%\AppData\Local\Temp
+ set /a TEST_JOBS=(15 * %NUMBER_OF_PROCESSORS% / 10) > nul
+ set | C:\msys64\usr\bin\sort > new.env
+ C:\msys64\usr\bin\comm -13 old.env new.env >> %GITHUB_ENV%
+ del *.env
+ - name: compiler version
+ run: cl
+ - name: link libraries
+ run: |
+ for %%I in (C:\vcpkg\installed\x64-windows\bin\*.dll) do (
+ if not %%~nI == readline mklink %%~nxI %%I
+ )
+ for %%I in (libcrypto-1_1-x64 libssl-1_1-x64) do (
+ ren c:\Windows\System32\%%I.dll %%I.dll_
+ )
+ - name: Configure
+ run: >-
+ ../src/win32/configure.bat --disable-install-doc
+ --with-opt-dir=C:/vcpkg/installed/x64-windows
+ - run: nmake incs
+ - run: nmake extract-extlibs
+ - run: nmake
+ env:
+ YACC: bison.exe
+ - run: nmake test
+ timeout-minutes: 5
+ - run: nmake test-spec
+ timeout-minutes: 10
+ - run: nmake test-all
+ env:
+ RUBY_TESTOPTS: -j${{env.TEST_JOBS}} --job-status=normal
+ timeout-minutes: 60
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "VS${{ matrix.vs }} / ${{ matrix.test_task || 'check' }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
+ shell: cmd
diff --git a/.github/workflows/yjit-ubuntu.yml b/.github/workflows/yjit-ubuntu.yml
new file mode 100644
index 0000000000..0b7b9046e9
--- /dev/null
+++ b/.github/workflows/yjit-ubuntu.yml
@@ -0,0 +1,170 @@
+name: YJIT Ubuntu
+on:
+ push:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ pull_request:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+ merge_group:
+ paths-ignore:
+ - 'doc/**'
+ - '**/man'
+ - '**.md'
+ - '**.rdoc'
+ - '**/.document'
+
+concurrency:
+ group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
+ cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
+
+permissions:
+ contents: read
+
+jobs:
+ cargo:
+ name: Rust cargo test
+ # GitHub Action's image seems to already contain a Rust 1.58.0.
+ runs-on: ubuntu-22.04
+ steps:
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ # For now we can't run cargo test --offline because it complains about the
+ # capstone dependency, even though the dependency is optional
+ #- run: cargo test --offline
+ - run: RUST_BACKTRACE=1 cargo test
+ working-directory: yjit
+ # Also compile and test with all features enabled
+ - run: RUST_BACKTRACE=1 cargo test --all-features
+ working-directory: yjit
+ # Check that we can build in release mode too
+ - run: cargo build --release
+ working-directory: yjit
+ make:
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - test_task: 'yjit-bindgen'
+ hint: 'To fix: use patch in logs'
+ configure: '--with-gcc=clang-14 --enable-yjit=dev'
+ libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1'
+
+ - test_task: "check"
+ # YJIT should be automatically built in release mode on x86-64 Linux with rustc present
+ #configure: "--enable-yjit RUSTC='rustc +1.58.0'"
+ configure: "RUSTC='rustc +1.58.0'"
+ rust_version: "1.58.0"
+
+ - test_task: "check"
+ configure: "--enable-yjit=dev"
+
+ - test_task: "check"
+ configure: "--enable-yjit=dev"
+ yjit_opts: "--yjit-call-threshold=1 --yjit-verify-ctx"
+
+ - test_task: "test-all TESTS=--repeat-count=2"
+ configure: "--enable-yjit=dev"
+
+ - test_task: "test-bundled-gems"
+ configure: "--enable-yjit=dev"
+
+ - test_task: "yjit-bench"
+ configure: "--enable-yjit=dev"
+ yjit_bench_opts: "--yjit-stats"
+ env:
+ GITPULLOPTIONS: --no-tags origin ${{github.ref}}
+ RUN_OPTS: ${{ matrix.yjit_opts }}
+ YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }}
+ RUBY_DEBUG: ci
+ BUNDLE_JOBS: 8 # for yjit-bench
+ runs-on: ubuntu-22.04
+ if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }}
+ steps:
+ - run: mkdir build
+ working-directory:
+ - name: Install libraries
+ run: |
+ set -x
+ sudo apt-get update -q || :
+ sudo apt-get install --no-install-recommends -q -y build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev bison autoconf ruby
+ - name: Install Rust
+ if: ${{ matrix.rust_version }}
+ run: rustup install ${{ matrix.rust_version }} --profile minimal
+ - name: git config
+ run: |
+ git config --global advice.detachedHead 0
+ git config --global init.defaultBranch garbage
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ path: src
+ - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
+ with:
+ path: src/.downloaded-cache
+ key: downloaded-cache
+ - name: Fixed world writable dirs
+ run: |
+ chmod -v go-w $HOME $HOME/.config
+ sudo chmod -R go-w /usr/share
+ sudo bash -c 'IFS=:; for d in '"$PATH"'; do chmod -v go-w $d; done' || :
+ - name: Set ENV
+ run: |
+ echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV
+ - run: ./autogen.sh
+ working-directory: src
+ - name: Run configure
+ run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install ${{ matrix.configure }}
+ - run: make incs
+ - run: make prepare-gems
+ if: ${{ matrix.test_task == 'test-bundled-gems' }}
+ - run: make -j
+ - run: make leaked-globals
+ if: ${{ matrix.test_task == 'check' }}
+ - name: Create dummy files in build dir
+ run: |
+ ./miniruby -e '(("a".."z").to_a+("A".."Z").to_a+("0".."9").to_a+%w[foo bar test zzz]).each{|basename|File.write("#{basename}.rb", "raise %(do not load #{basename}.rb)")}'
+ if: ${{ matrix.test_task == 'check' }}
+ - name: Enable YJIT through ENV
+ run: echo "RUBY_YJIT_ENABLE=1" >> $GITHUB_ENV
+ # Check that the binary was built with YJIT
+ - name: Check YJIT enabled
+ run: ./miniruby --yjit -v | grep "+YJIT"
+ - name: make ${{ matrix.test_task }}
+ run: make -s -j ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" YJIT_BENCH_OPTS="$YJIT_BENCH_OPTS"
+ timeout-minutes: 60
+ env:
+ RUBY_TESTOPTS: "-q --tty=no"
+ TEST_BUNDLED_GEMS_ALLOW_FAILURES: ""
+ PRECHECK_BUNDLED_GEMS: "no"
+ LIBCLANG_PATH: ${{ matrix.libclang_path }}
+ continue-on-error: ${{ matrix.test_task == 'yjit-bench' }}
+ - name: Show ${{ github.event.pull_request.base.ref }} GitHub URL for yjit-bench comparison
+ run: echo "https://github.com/${BASE_REPO}/commit/${BASE_SHA}"
+ env:
+ BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }}
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
+ if: ${{ matrix.test_task == 'yjit-bench' && startsWith(github.event_name, 'pull') }}
+ - uses: ruby/action-slack@0bd85c72233cdbb6a0fe01d37aaeff1d21b5fce1 # v3.2.1
+ with:
+ payload: |
+ {
+ "ci": "GitHub Actions",
+ "env": "${{ github.workflow }} / ${{ matrix.test_task }} ${{ matrix.configure }}",
+ "url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
+ "commit": "${{ github.sha }}",
+ "branch": "${{ github.ref_name }}"
+ }
+ env:
+ SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
+ if: ${{ failure() && github.event_name == 'push' }}
+
+defaults:
+ run:
+ working-directory: build
diff --git a/.gitignore b/.gitignore
index 9849721d2a..99d32a1825 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,26 +4,32 @@
*.a
*.bak
*.bc
+*.bundle
*.dSYM
*.dmyh
*.dylib
*.elc
*.i
+*.ii
*.inc
*.log
*.o
+*.o.tmp
*.obj
*.old
*.orig
*.pch
*.pdb
+*.rbinc
*.rej
*.s
*.sav
+*.sl
+*.so
+*.so.*
*.swp
*.yarb
*~
-.*-*
.*.list
.*.time
.DS_Store
@@ -33,6 +39,8 @@
.pc
.ppack
.svn
+.time
+.ruby-version
Makefile
cygruby*.def
extconf.h
@@ -41,6 +49,7 @@ y.tab.c
*.gcda
*.gcno
*.gcov
+*.vscode
lcov*.info
# /
@@ -51,7 +60,11 @@ lcov*.info
/*.pc
/*.rc
/*_prelude.c
+/.downloaded-cache
+/.top-enc.mk
+/build*/
/COPYING.LIB
+/ChangeLog
/Doxyfile
/GNUmakefile
/README.atheos
@@ -109,14 +122,17 @@ lcov*.info
/rename2.h
/repack
/revision.h
+/revision.tmp
/riscos
/rubicon
/ruby
/ruby-runner
/ruby-runner.h
/ruby-man.rd.gz
+/rubyspec_temp
/run.gdb
/sizes.c
+/static-ruby
/test.rb
/test-coverage.dat
/tmp
@@ -127,6 +143,13 @@ lcov*.info
/web
/yasmdata.rb
+# /bin/
+
+/bin/*.exe
+/bin/*.dll
+/bin/goruby
+/bin/ruby
+
# /benchmark/
/benchmark/bm_require.data
/benchmark/bmx_*.rb
@@ -139,27 +162,35 @@ lcov*.info
/enc/jis/props.h
/enc/unicode/data
+# /coroutine/
+!/coroutine/**/*.s
+
# /enc/trans/
/enc/trans/*.c
/enc/trans/*.def
/enc/trans/*.exp
/enc/trans/*.lib
-/enc/trans/.time
# /exe/
+/exe/goruby
/exe/ruby
-/exe/.time
# /ext/
/ext/extinit.c
/ext/configure-ext.mk
/ext/*/exts.mk
+# /ext/-test-/cxxanyargs
+/ext/-test-/cxxanyargs/failure*.failed
+
# /ext/-test-/win32/dln/
/ext/-test-/win32/dln/dlntest.dll
/ext/-test-/win32/dln/dlntest.exp
/ext/-test-/win32/dln/dlntest.lib
+# /ext/-test-/gems
+/ext/-test-/gems
+
# /ext/etc/
/ext/etc/constdefs.h
@@ -172,6 +203,7 @@ lcov*.info
# /ext/ripper/
/ext/ripper/eventids1.c
+/ext/ripper/.eventids2-check
/ext/ripper/eventids2table.c
/ext/ripper/ripper.*
/ext/ripper/ids1
@@ -187,6 +219,13 @@ lcov*.info
/gems/src
/gems/*-*
+# /lib/
+/lib/ruby/[1-9]*.*
+/lib/ruby/vendor_ruby
+
+# /misc/
+/misc/**/__pycache__
+
# /spec/bundler
/.rspec_status
@@ -196,9 +235,16 @@ lcov*.info
# /win32/
/win32/*.ico
-/win32/.time
# MJIT
-/rb_mjit_header.h
-/mjit_config.h
/include/ruby-*/*/rb_mjit_min_header-*.h
+/lib/ruby_vm/mjit/instruction.rb
+/mjit_config.h
+/rb_mjit_header.h
+
+# YJIT
+/yjit-bench
+/yjit_exit_locations.dump
+
+# /wasm/
+/wasm/tests/*.wasm
diff --git a/.indent.pro b/.indent.pro
index 6a207a0554..1d61cbcad1 100644
--- a/.indent.pro
+++ b/.indent.pro
@@ -2,12 +2,15 @@
-nbbb
-nbc
-br
+-brs
-nbs
-ncdb
--ce
--cli0.5
+-nce
+-cdw
+-cli2
+-cbi2
-ndj
--ei
+-ncs
-nfc1
-i4
-l120
@@ -16,6 +19,14 @@
-psl
-sc
-sob
+-sbi4
+-nut
+-par
-TID
-TVALUE
+-Tst_data_t
+-Tst_index_t
+-Tst_table
+-Trb_data_type_t
+-TFILE
diff --git a/.rdoc_options b/.rdoc_options
new file mode 100644
index 0000000000..760507c7a2
--- /dev/null
+++ b/.rdoc_options
@@ -0,0 +1,4 @@
+---
+page_dir: doc
+main_page: README.md
+title: Documentation for Ruby development version
diff --git a/.rspec_parallel b/.rspec_parallel
new file mode 100644
index 0000000000..aaff198a32
--- /dev/null
+++ b/.rspec_parallel
@@ -0,0 +1,2 @@
+--format progress
+--format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index a6ba91a1c4..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,123 +0,0 @@
-# Copyright (C) 2011 Urabe, Shyouhei. All rights reserved.
-#
-# This file is a part of the programming language Ruby. Permission is hereby
-# granted, to either redistribute or modify this file, provided that the
-# conditions mentioned in the file COPYING are met. Consult the file for
-# details.
-
-# This is a Travis-CI build configuration file. The list of configurations
-# available is located in
-#
-# http://about.travis-ci.org/docs/user/build-configuration/
-#
-# and as Ruby itself is a project written in C language,
-#
-# http://about.travis-ci.org/docs/user/languages/c/
-#
-# is also a good place to look at.
-
-language: c
-dist: trusty
-
-sudo: false
-
-matrix:
- include:
- - os: linux
- compiler: gcc
- - os: linux
- compiler: gcc-8
- addons:
- apt:
- sources:
- - ubuntu-toolchain-r-test
- packages:
- - gcc-8
- - os: linux
- language: ruby
- rvm: 2.3
- name: Running ruby/spec on 2.3 to ensure version guards are correctly added
- before_install:
- install:
- before_script: chmod -R u+w spec/ruby
- script: ruby -C spec/ruby ../mspec/bin/mspec -j .
-
-before_install:
- - "CONFIG_FLAG="
- - "JOBS=-j`nproc`"
-
-before_script:
- - "echo JOBS=$JOBS"
- - "uname -a"
- - "uname -r"
- - "rm -fr .ext autom4te.cache"
- - "echo $TERM"
- - "> config.status"
- - "sed -f tool/prereq.status Makefile.in common.mk > Makefile"
- - "make update-config_files"
- - "make touch-unicode-files"
- - "make -s $JOBS srcs UNICODE_FILES=."
- - "requests=; for req in ${RUBYSPEC_PULL_REQUEST//,/ }; do
- requests=\"$requests +refs/pull/$req/merge:\";
- done"
- - "${requests:+git -C spec/ruby -c user.email=none -c user.name=none pull --no-edit origin $requests}"
- - "${requests:+git -C spec/ruby log --oneline origin/master..@}"
- - "rm config.status Makefile rbconfig.rb .rbconfig.time"
- - "mkdir build config_1st config_2nd"
- - "chmod -R a-w ."
- - "chmod u+w build config_1st config_2nd"
- - "cd build"
- - "../configure -C --disable-install-doc --prefix=/tmp/ruby-prefix --with-gcc=$CC $CONFIG_FLAG"
- - "cp -pr config.cache config.status .ext/include ../config_1st"
- - "make reconfig"
- - "cp -pr config.cache config.status .ext/include ../config_2nd"
- - "(cd .. && exec diff -ru config_1st config_2nd)"
- - "make -s $JOBS && make install"
-
-script:
- - "make -s test TESTOPTS=--color=never"
- - "make -s $JOBS test-all -o exts TESTOPTS='-q --color=never --job-status=normal' RUBY_FORCE_TEST_JIT=1"
- - "make -s $JOBS test-spec MSPECOPT=-j"
-
-# Branch matrix. Not all branches are Travis-ready so we limit branches here.
-branches:
- only:
- - trunk
- - ruby_2_2
- - ruby_2_3
- - ruby_2_4
- - ruby_2_5
- - /^feature\//
- - /^bug\//
- - /^travis-/
-
-# We want to be notified when something happens.
-notifications:
- irc:
- channels:
- - "chat.freenode.net#ruby-core"
- - "chat.freenode.net#ruby-ja"
- on_success: change # [always|never|change] # default: always
- on_failure: always # [always|never|change] # default: always
- template:
- - "%{message} by @%{author}: See %{build_url}"
-
- slack:
- rooms:
- - secure: i1GLETSKye85ea6dGNA3MxI/5myChmMFiZtBd5C69xK+s1sBFqEgOSbaSf9KHc0CYrHVyNhQMaZRruieV7xS+6Pfs0Zvxf1DO6QQTWC2KhkqwFDLvZncAzjoyASdR90hbr+iRPOngQ+HJuE94zemALAwEqNAinzA74PMiJXktqY= # ruby:<token>#commits
- - secure: ah7UEHBvncXT7bM5mvYIQAO+tIyV/wl7nXLb7wQD16dO2v8Gragy0mWjB79Q09hrrMGmp6H9bCDpdGS80boIA5EHaHoG4QaP0i9bsSt8U2AMWgZtfyIgQKJ4H2kXkGlrjO+AXTgnIkP7LNjdgAVUUTGQPb26T3QmoN2Splt+fIQ= # ruby:<token>#alerts
- on_pull_requests: false
- on_success: change
- on_failure: always
-
- email:
- - ko1c-failure@atdot.net
-
-# Local Variables:
-# mode: YAML
-# coding: utf-8-unix
-# indent-tabs-mode: nil
-# tab-width: 4
-# fill-column: 79
-# default-justification: full
-# End:
diff --git a/BSDL b/BSDL
index a009caefea..66d93598aa 100644
--- a/BSDL
+++ b/BSDL
@@ -4,10 +4,10 @@ Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
+ notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
-notice, this list of conditions and the following disclaimer in the
-documentation and/or other materials provided with the distribution.
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ffdf2dd4b8..13df6087ca 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1 @@
-Please see the [official issue tracker] and wiki [HowToContribute].
-
-[official issue tracker]: https://bugs.ruby-lang.org
-[HowToContribute]: https://bugs.ruby-lang.org/projects/ruby/wiki/HowToContribute
+See ["Contributing to Ruby"](https://docs.ruby-lang.org/en/master/contributing_md.html), which includes setup and build instructions.
diff --git a/COPYING b/COPYING
index f06056fb45..48e5a96de7 100644
--- a/COPYING
+++ b/COPYING
@@ -2,55 +2,55 @@ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
You can redistribute it and/or modify it under either the terms of the
2-clause BSDL (see the file BSDL), or the conditions below:
- 1. You may make and give away verbatim copies of the source form of the
- software without restriction, provided that you duplicate all of the
- original copyright notices and associated disclaimers.
+1. You may make and give away verbatim copies of the source form of the
+ software without restriction, provided that you duplicate all of the
+ original copyright notices and associated disclaimers.
- 2. You may modify your copy of the software in any way, provided that
- you do at least ONE of the following:
+2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
- a) place your modifications in the Public Domain or otherwise
- make them Freely Available, such as by posting said
- modifications to Usenet or an equivalent medium, or by allowing
- the author to include your modifications in the software.
+ a. place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
- b) use the modified software only within your corporation or
- organization.
+ b. use the modified software only within your corporation or
+ organization.
- c) give non-standard binaries non-standard names, with
- instructions on where to get the original software distribution.
+ c. give non-standard binaries non-standard names, with
+ instructions on where to get the original software distribution.
- d) make other distribution arrangements with the author.
+ d. make other distribution arrangements with the author.
- 3. You may distribute the software in object code or binary form,
- provided that you do at least ONE of the following:
+3. You may distribute the software in object code or binary form,
+ provided that you do at least ONE of the following:
- a) distribute the binaries and library files of the software,
- together with instructions (in the manual page or equivalent)
- on where to get the original distribution.
+ a. distribute the binaries and library files of the software,
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
- b) accompany the distribution with the machine-readable source of
- the software.
+ b. accompany the distribution with the machine-readable source of
+ the software.
- c) give non-standard binaries non-standard names, with
- instructions on where to get the original software distribution.
+ c. give non-standard binaries non-standard names, with
+ instructions on where to get the original software distribution.
- d) make other distribution arrangements with the author.
+ d. make other distribution arrangements with the author.
- 4. You may modify and include the part of the software into any other
- software (possibly commercial). But some files in the distribution
- are not written by the author, so that they are not under these terms.
+4. You may modify and include the part of the software into any other
+ software (possibly commercial). But some files in the distribution
+ are not written by the author, so that they are not under these terms.
- For the list of those files and their copying conditions, see the
- file LEGAL.
+ For the list of those files and their copying conditions, see the
+ file LEGAL.
- 5. The scripts and library files supplied as input to or produced as
- output from the software do not automatically fall under the
- copyright of the software, but belong to whomever generated them,
- and may be sold commercially, and may be aggregated with this
- software.
+5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whomever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
- 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE.
+6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
diff --git a/COPYING.ja b/COPYING.ja
index e50d01c8d1..230376bc60 100644
--- a/COPYING.ja
+++ b/COPYING.ja
@@ -2,50 +2,50 @@
ã¾ãŸã¯ä»¥ä¸‹ã«ç¤ºã™æ¡ä»¶ã§æœ¬ãƒ—ログラムをå†é…布ã§ãã¾ã™
2-clause BSDLã«ã¤ã„ã¦ã¯BSDLファイルをå‚ç…§ã—ã¦ä¸‹ã•ã„.
- 1. 複製ã¯åˆ¶é™ãªã自由ã§ã™ï¼Ž
+1. 複製ã¯åˆ¶é™ãªã自由ã§ã™ï¼Ž
- 2. ä»¥ä¸‹ã®æ¡ä»¶ã®ã„ãšã‚Œã‹ã‚’満ãŸã™æ™‚ã«æœ¬ãƒ—ログラムã®ã‚½ãƒ¼ã‚¹ã‚’
- 自由ã«å¤‰æ›´ã§ãã¾ã™ï¼Ž
+2. ä»¥ä¸‹ã®æ¡ä»¶ã®ã„ãšã‚Œã‹ã‚’満ãŸã™æ™‚ã«æœ¬ãƒ—ログラムã®ã‚½ãƒ¼ã‚¹ã‚’
+ 自由ã«å¤‰æ›´ã§ãã¾ã™ï¼Ž
- (a) ãƒãƒƒãƒˆãƒ‹ãƒ¥ãƒ¼ã‚ºã«ãƒã‚¹ãƒˆã—ãŸã‚Šï¼Œä½œè€…ã«å¤‰æ›´ã‚’é€ä»˜ã™ã‚‹
- ãªã©ã®æ–¹æ³•ã§ï¼Œå¤‰æ›´ã‚’公開ã™ã‚‹ï¼Ž
+ a. ãƒãƒƒãƒˆãƒ‹ãƒ¥ãƒ¼ã‚ºã«ãƒã‚¹ãƒˆã—ãŸã‚Šï¼Œä½œè€…ã«å¤‰æ›´ã‚’é€ä»˜ã™ã‚‹
+ ãªã©ã®æ–¹æ³•ã§ï¼Œå¤‰æ›´ã‚’公開ã™ã‚‹ï¼Ž
- (b) 変更ã—ãŸæœ¬ãƒ—ãƒ­ã‚°ãƒ©ãƒ ã‚’è‡ªåˆ†ã®æ‰€å±žã™ã‚‹çµ„織内部ã ã‘ã§
- 使ã†ï¼Ž
+ b. 変更ã—ãŸæœ¬ãƒ—ãƒ­ã‚°ãƒ©ãƒ ã‚’è‡ªåˆ†ã®æ‰€å±žã™ã‚‹çµ„織内部ã ã‘ã§
+ 使ã†ï¼Ž
- (c) 変更点を明示ã—ãŸã†ãˆï¼Œã‚½ãƒ•トウェアã®åå‰ã‚’変更ã™ã‚‹ï¼Ž
- ãã®ã‚½ãƒ•トウェアをé…布ã™ã‚‹æ™‚ã«ã¯å¤‰æ›´å‰ã®æœ¬ãƒ—ログラ
- ãƒ ã‚‚åŒæ™‚ã«é…布ã™ã‚‹ï¼Žã¾ãŸã¯å¤‰æ›´å‰ã®æœ¬ãƒ—ログラムã®ã‚½ãƒ¼
- スã®å…¥æ‰‹æ³•を明示ã™ã‚‹ï¼Ž
+ c. 変更点を明示ã—ãŸã†ãˆï¼Œã‚½ãƒ•トウェアã®åå‰ã‚’変更ã™ã‚‹ï¼Ž
+ ãã®ã‚½ãƒ•トウェアをé…布ã™ã‚‹æ™‚ã«ã¯å¤‰æ›´å‰ã®æœ¬ãƒ—ログラ
+ ãƒ ã‚‚åŒæ™‚ã«é…布ã™ã‚‹ï¼Žã¾ãŸã¯å¤‰æ›´å‰ã®æœ¬ãƒ—ログラムã®ã‚½ãƒ¼
+ スã®å…¥æ‰‹æ³•を明示ã™ã‚‹ï¼Ž
- (d) ãã®ä»–ã®å¤‰æ›´æ¡ä»¶ã‚’作者ã¨åˆæ„ã™ã‚‹ï¼Ž
+ d. ãã®ä»–ã®å¤‰æ›´æ¡ä»¶ã‚’作者ã¨åˆæ„ã™ã‚‹ï¼Ž
- 3. ä»¥ä¸‹ã®æ¡ä»¶ã®ã„ãšã‚Œã‹ã‚’満ãŸã™æ™‚ã«æœ¬ãƒ—ログラムをコンパイ
- ルã—ãŸã‚ªãƒ–ジェクトコードや実行形å¼ã§ã‚‚é…布ã§ãã¾ã™ï¼Ž
+3. ä»¥ä¸‹ã®æ¡ä»¶ã®ã„ãšã‚Œã‹ã‚’満ãŸã™æ™‚ã«æœ¬ãƒ—ログラムをコンパイ
+ ルã—ãŸã‚ªãƒ–ジェクトコードや実行形å¼ã§ã‚‚é…布ã§ãã¾ã™ï¼Ž
- (a) ãƒã‚¤ãƒŠãƒªã‚’å—ã‘å–ã£ãŸäººãŒã‚½ãƒ¼ã‚¹ã‚’入手ã§ãるよã†ã«ï¼Œ
- ソースã®å…¥æ‰‹æ³•を明示ã™ã‚‹ï¼Ž
+ a. ãƒã‚¤ãƒŠãƒªã‚’å—ã‘å–ã£ãŸäººãŒã‚½ãƒ¼ã‚¹ã‚’入手ã§ãるよã†ã«ï¼Œ
+ ソースã®å…¥æ‰‹æ³•を明示ã™ã‚‹ï¼Ž
- (b) 機械å¯èª­ãªã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’添付ã™ã‚‹ï¼Ž
+ b. 機械å¯èª­ãªã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã‚’添付ã™ã‚‹ï¼Ž
- (c) 変更を行ã£ãŸãƒã‚¤ãƒŠãƒªã¯åå‰ã‚’変更ã—ãŸã†ãˆï¼Œã‚ªãƒªã‚¸ãƒŠ
- ルã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã®å…¥æ‰‹æ³•を明示ã™ã‚‹ï¼Ž
+ c. 変更を行ã£ãŸãƒã‚¤ãƒŠãƒªã¯åå‰ã‚’変更ã—ãŸã†ãˆï¼Œã‚ªãƒªã‚¸ãƒŠ
+ ルã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã®å…¥æ‰‹æ³•を明示ã™ã‚‹ï¼Ž
- (d) ãã®ä»–ã®é…布æ¡ä»¶ã‚’作者ã¨åˆæ„ã™ã‚‹ï¼Ž
+ d. ãã®ä»–ã®é…布æ¡ä»¶ã‚’作者ã¨åˆæ„ã™ã‚‹ï¼Ž
- 4. ä»–ã®ãƒ—ログラムã¸ã®å¼•用ã¯ã„ã‹ãªã‚‹ç›®çš„ã§ã‚れ自由ã§ã™ï¼ŽãŸ
- ã ã—,本プログラムã«å«ã¾ã‚Œã‚‹ä»–ã®ä½œè€…ã«ã‚ˆã‚‹ã‚³ãƒ¼ãƒ‰ã¯ï¼Œã
- れãžã‚Œã®ä½œè€…ã®æ„å‘ã«ã‚ˆã‚‹åˆ¶é™ãŒåŠ ãˆã‚‰ã‚Œã‚‹å ´åˆãŒã‚りã¾ã™ï¼Ž
+4. ä»–ã®ãƒ—ログラムã¸ã®å¼•用ã¯ã„ã‹ãªã‚‹ç›®çš„ã§ã‚れ自由ã§ã™ï¼ŽãŸ
+ ã ã—,本プログラムã«å«ã¾ã‚Œã‚‹ä»–ã®ä½œè€…ã«ã‚ˆã‚‹ã‚³ãƒ¼ãƒ‰ã¯ï¼Œã
+ れãžã‚Œã®ä½œè€…ã®æ„å‘ã«ã‚ˆã‚‹åˆ¶é™ãŒåŠ ãˆã‚‰ã‚Œã‚‹å ´åˆãŒã‚りã¾ã™ï¼Ž
- ãれらファイルã®ä¸€è¦§ã¨ãれãžã‚Œã®é…布æ¡ä»¶ãªã©ã«ä»˜ã„ã¦ã¯
- LEGALファイルをå‚ç…§ã—ã¦ãã ã•ã„.
+ ãれらファイルã®ä¸€è¦§ã¨ãれãžã‚Œã®é…布æ¡ä»¶ãªã©ã«ä»˜ã„ã¦ã¯
+ LEGALファイルをå‚ç…§ã—ã¦ãã ã•ã„.
- 5. 本プログラムã¸ã®å…¥åŠ›ã¨ãªã‚‹ã‚¹ã‚¯ãƒªãƒ—トãŠã‚ˆã³ï¼Œæœ¬ãƒ—ログラ
- ムã‹ã‚‰ã®å‡ºåŠ›ã®æ¨©åˆ©ã¯æœ¬ãƒ—ログラムã®ä½œè€…ã§ã¯ãªã,ãれãž
- れã®å…¥å‡ºåŠ›ã‚’ç”Ÿæˆã—ãŸäººã«å±žã—ã¾ã™ï¼Žã¾ãŸï¼Œæœ¬ãƒ—ログラムã«
- 組ã¿è¾¼ã¾ã‚Œã‚‹ãŸã‚ã®æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã«ã¤ã„ã¦ã‚‚åŒæ§˜ã§ã™ï¼Ž
+5. 本プログラムã¸ã®å…¥åŠ›ã¨ãªã‚‹ã‚¹ã‚¯ãƒªãƒ—トãŠã‚ˆã³ï¼Œæœ¬ãƒ—ログラ
+ ムã‹ã‚‰ã®å‡ºåŠ›ã®æ¨©åˆ©ã¯æœ¬ãƒ—ログラムã®ä½œè€…ã§ã¯ãªã,ãれãž
+ れã®å…¥å‡ºåŠ›ã‚’ç”Ÿæˆã—ãŸäººã«å±žã—ã¾ã™ï¼Žã¾ãŸï¼Œæœ¬ãƒ—ログラムã«
+ 組ã¿è¾¼ã¾ã‚Œã‚‹ãŸã‚ã®æ‹¡å¼µãƒ©ã‚¤ãƒ–ラリã«ã¤ã„ã¦ã‚‚åŒæ§˜ã§ã™ï¼Ž
- 6. 本プログラムã¯ç„¡ä¿è¨¼ã§ã™ï¼Žä½œè€…ã¯æœ¬ãƒ—ログラムをサãƒãƒ¼ãƒˆ
- ã™ã‚‹æ„å¿—ã¯ã‚りã¾ã™ãŒï¼Œãƒ—ログラム自身ã®ãƒã‚°ã‚ã‚‹ã„ã¯æœ¬ãƒ—
- ログラムã®å®Ÿè¡Œãªã©ã‹ã‚‰ç™ºç”Ÿã™ã‚‹ã„ã‹ãªã‚‹æå®³ã«å¯¾ã—ã¦ã‚‚責
- 任をæŒã¡ã¾ã›ã‚“.
+6. 本プログラムã¯ç„¡ä¿è¨¼ã§ã™ï¼Žä½œè€…ã¯æœ¬ãƒ—ログラムをサãƒãƒ¼ãƒˆ
+ ã™ã‚‹æ„å¿—ã¯ã‚りã¾ã™ãŒï¼Œãƒ—ログラム自身ã®ãƒã‚°ã‚ã‚‹ã„ã¯æœ¬ãƒ—
+ ログラムã®å®Ÿè¡Œãªã©ã‹ã‚‰ç™ºç”Ÿã™ã‚‹ã„ã‹ãªã‚‹æå®³ã«å¯¾ã—ã¦ã‚‚責
+ 任をæŒã¡ã¾ã›ã‚“.
diff --git a/LEGAL b/LEGAL
index 0479751691..0423d57ac9 100644
--- a/LEGAL
+++ b/LEGAL
@@ -1,159 +1,248 @@
-LEGAL NOTICE INFORMATION
-------------------------
+# -*- rdoc -*-
+
+= LEGAL NOTICE INFORMATION
+--------------------------
All the files in this distribution are covered under either the Ruby's
license (see the file COPYING) or public-domain except some files
mentioned below.
-ccan/build_assert/build_assert.h
-ccan/check_type/check_type.h
-ccan/container_of/container_of.h
-ccan/str/str.h
+[addr2line.c]
+
+ A part of this file is from FreeBSD.
+
+ >>>
+ Copyright (c) 1986, 1988, 1991, 1993::
+ The Regents of the University of California. All rights reserved.
- These files are licensed under the CC0.
+ (c) UNIX System Laboratories, Inc.
- https://creativecommons.org/choose/zero/
+ All or some portions of this file are derived from material licensed
+ to the University of California by American Telephone and Telegraph
+ Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ the permission of UNIX System Laboratories, Inc.
-ccan/list/list.h
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
- This file is licensed under the MIT License.
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
+ @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
+
+
+[ccan/build_assert/build_assert.h]
+[ccan/check_type/check_type.h]
+[ccan/container_of/container_of.h]
+[ccan/str/str.h]
+
+ These files are licensed under the {CC0}[https://creativecommons.org/choose/zero/].
+
+[ccan/list/list.h]
+
+ This file is licensed under the {MIT License}[rdoc-label:label-MIT+License].
+
+[coroutine]
+
+ Unless otherwise specified, these files are licensed under the
+ {MIT License}[rdoc-label:label-MIT+License].
+
+[include/ruby/onigmo.h]
+[include/ruby/oniguruma.h]
+[regcomp.c]
+[regenc.c]
+[regenc.h]
+[regerror.c]
+[regexec.c]
+[regint.h]
+[regparse.c]
+[regparse.h]
+[enc/ascii.c]
+[enc/big5.c]
+[enc/cp949.c]
+[enc/emacs_mule.c]
+[enc/encdb.c]
+[enc/euc_jp.c]
+[enc/euc_kr.c]
+[enc/euc_tw.c]
+[enc/gb18030.c]
+[enc/gb2312.c]
+[enc/gbk.c]
+[enc/iso_8859_1.c]
+[enc/iso_8859_10.c]
+[enc/iso_8859_11.c]
+[enc/iso_8859_13.c]
+[enc/iso_8859_14.c]
+[enc/iso_8859_15.c]
+[enc/iso_8859_16.c]
+[enc/iso_8859_2.c]
+[enc/iso_8859_3.c]
+[enc/iso_8859_4.c]
+[enc/iso_8859_5.c]
+[enc/iso_8859_6.c]
+[enc/iso_8859_7.c]
+[enc/iso_8859_8.c]
+[enc/iso_8859_9.c]
+[enc/koi8_r.c]
+[enc/koi8_u.c]
+[enc/shift_jis.c]
+[enc/unicode.c]
+[enc/us_ascii.c]
+[enc/utf_16be.c]
+[enc/utf_16le.c]
+[enc/utf_32be.c]
+[enc/utf_32le.c]
+[enc/utf_8.c]
+[enc/windows_1251.c]
+[enc/windows_31j.c]
+
+ Onigmo (Oniguruma-mod) LICENSE
+
+ >>>
+ Copyright (c) 2002-2009:: K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ Copyright (c) 2011-2014:: K.Takata <kentkt AT csc DOT jp>
+ All rights reserved.
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-
-include/ruby/onigmo.h:
-include/ruby/oniguruma.h:
-regcomp.c:
-regenc.[ch]:
-regerror.c:
-regexec.c:
-regint.h:
-regparse.[ch]:
-enc/ascii.c
-enc/big5.c
-enc/cp949.c
-enc/emacs_mule.c
-enc/encdb.c
-enc/euc_jp.c
-enc/euc_kr.c
-enc/euc_tw.c
-enc/gb18030.c
-enc/gb2312.c
-enc/gbk.c
-enc/iso_8859_1.c
-enc/iso_8859_10.c
-enc/iso_8859_11.c
-enc/iso_8859_13.c
-enc/iso_8859_14.c
-enc/iso_8859_15.c
-enc/iso_8859_16.c
-enc/iso_8859_2.c
-enc/iso_8859_3.c
-enc/iso_8859_4.c
-enc/iso_8859_5.c
-enc/iso_8859_6.c
-enc/iso_8859_7.c
-enc/iso_8859_8.c
-enc/iso_8859_9.c
-enc/koi8_r.c
-enc/koi8_u.c
-enc/shift_jis.c
-enc/unicode.c
-enc/us_ascii.c
-enc/utf_16be.c
-enc/utf_16le.c
-enc/utf_32be.c
-enc/utf_32le.c
-enc/utf_8.c
-enc/windows_1251.c
-
-Onigmo (Oniguruma-mod) LICENSE
-------------------------------
-
-/*-
- * Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * Copyright (c) 2011-2014 K.Takata <kentkt AT csc DOT jp>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-
-Oniguruma LICENSE
------------------
-
-/*-
- * Copyright (c) 2002-2009 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-https://github.com/k-takata/Onigmo/
-https://github.com/kkos/oniguruma
-https://svnweb.freebsd.org/ports/head/devel/oniguruma/
-
- When this software is partly used or it is distributed with Ruby,
- this of Ruby follows the license of Ruby.
-
-enc/trans/GB/GB12345%UCS.src:
-enc/trans/GB/UCS%GB12345.src:
-enc/trans/GB/GB2312%UCS.src:
-enc/trans/GB/UCS%GB2312.src:
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ Oniguruma LICENSE
+
+ >>>
+ Copyright (c) 2002-2009:: K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ * https://github.com/k-takata/Onigmo/
+ * https://github.com/kkos/oniguruma
+ * https://svnweb.freebsd.org/ports/head/devel/oniguruma/
+
+ When this software is partly used or it is distributed with Ruby,
+ this of Ruby follows the license of Ruby.
+
+[enc/windows_1250.c]
+[enc/windows_1252.c]
+
+ >>>
+ Copyright (c) 2006-2007:: Byte <byte AT mail DOT kna DOT ru>
+ K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+[enc/cesu_8.c]
+[enc/windows_1253.c]
+[enc/windows_1254.c]
+[enc/windows_1257.c]
+
+ >>>
+ Copyright (c) 2002-2007:: K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+[enc/trans/GB/GB12345%UCS.src]
+[enc/trans/GB/UCS%GB12345.src]
+[enc/trans/GB/GB2312%UCS.src]
+[enc/trans/GB/UCS%GB2312.src]
+
+ These files have this explanatory texts.
+
+ >>>
This mapping data was created from files provided by Unicode, Inc.
(The Unicode Consortium). The files were used to create a product supporting
Unicode, as explicitly permitted in the files' copyright notices.
@@ -161,56 +250,82 @@ enc/trans/GB/UCS%GB2312.src:
files for any particular purpose, and has ceased to publish the files many
years ago.
-enc/trans/JIS/JISX0201-KANA%UCS.src:
-enc/trans/JIS/JISX0208@1990%UCS.src:
-enc/trans/JIS/JISX0212%UCS.src:
-enc/trans/JIS/UCS%JISX0201-KANA.src:
-enc/trans/JIS/UCS%JISX0208@1990.src:
-enc/trans/JIS/UCS%JISX0212.src:
+[enc/trans/JIS/JISX0201-KANA%UCS.src]
+[enc/trans/JIS/JISX0208\@1990%UCS.src]
+[enc/trans/JIS/JISX0212%UCS.src]
+[enc/trans/JIS/UCS%JISX0201-KANA.src]
+[enc/trans/JIS/UCS%JISX0208@1990.src]
+[enc/trans/JIS/UCS%JISX0212.src]
+
+ These files are copyrighted as the following.
+ >>>
© 2015 Unicode®, Inc.
+
For terms of use, see http://www.unicode.org/terms_of_use.html
-enc/trans/JIS/JISX0213-1%UCS@BMP.src:
-enc/trans/JIS/JISX0213-1%UCS@SIP.src:
-enc/trans/JIS/JISX0213-2%UCS@BMP.src:
-enc/trans/JIS/JISX0213-2%UCS@SIP.src:
+[enc/trans/JIS/JISX0213-1%UCS@BMP.src]
+[enc/trans/JIS/JISX0213-1%UCS@SIP.src]
+[enc/trans/JIS/JISX0213-2%UCS@BMP.src]
+[enc/trans/JIS/JISX0213-2%UCS@SIP.src]
+
+ These files are copyrighted as the following.
+
+ >>>
+ Copyright (C) 2001:: earthian@tama.or.jp, All Rights Reserved.
+ Copyright (C) 2001:: I'O, All Rights Reserved.
+ Copyright (C) 2006:: Project X0213, All Rights Reserved.
+ You can use, modify, distribute this table freely.
- Copyright (C) 2001 earthian@tama.or.jp, All Rights Reserved.
- Copyright (C) 2001 I'O, All Rights Reserved.
- Copyright (C) 2006 Project X0213, All Rights Reserved.
- You can use, modify, distribute this table freely.
+[enc/trans/JIS/UCS@BMP%JISX0213-1.src]
+[enc/trans/JIS/UCS@BMP%JISX0213-2.src]
+[enc/trans/JIS/UCS@SIP%JISX0213-1.src]
+[enc/trans/JIS/UCS@SIP%JISX0213-2.src]
-enc/trans/JIS/UCS@BMP%JISX0213-1.src:
-enc/trans/JIS/UCS@BMP%JISX0213-2.src:
-enc/trans/JIS/UCS@SIP%JISX0213-1.src:
-enc/trans/JIS/UCS@SIP%JISX0213-2.src:
+ These files are copyrighted as the following.
- Copyright (C) 2001 earthian@tama.or.jp, All Rights Reserved.
- Copyright (C) 2001 I'O, All Rights Reserved.
- You can use, modify, distribute this table freely.
+ >>>
+ Copyright (C) 2001:: earthian@tama.or.jp, All Rights Reserved.
+ Copyright (C) 2001:: I'O, All Rights Reserved.
+ You can use, modify, distribute this table freely.
-configure:
+[enc/trans/ucm/glibc-BIG5-2.3.3.ucm]
+[enc/trans/ucm/glibc-BIG5HKSCS-2.3.3.ucm]
+
+ >>>
+ Copyright (C) 2001-2005:: International Business Machines
+ Corporation and others. All Rights Reserved.
+
+[enc/trans/ucm/windows-950-2000.ucm]
+[enc/trans/ucm/windows-950_hkscs-2001.ucm]
+
+ >>>
+ Copyright (C) 2001-2002:: International Business Machines
+ Corporation and others. All Rights Reserved.
+
+
+[configure]
This file is free software.
- Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+ >>>
+ Copyright (C) 1992-1996, 1998-2012:: Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
-tool/config.guess:
-tool/config.sub:
+[tool/config.guess]
+[tool/config.sub]
As long as you distribute these files with the file configure, they
are covered under the Ruby's license.
- Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999
- Free Software Foundation, Inc.
+ >>>
+ Copyright 1992-2018:: Free Software Foundation, Inc.
This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
@@ -219,54 +334,85 @@ tool/config.sub:
General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ along with this program; if not, see <https://www.gnu.org/licenses/>.
As a special exception to the GNU General Public License, if you
distribute this file as part of a program that contains a
configuration script generated by Autoconf, you may include it under
- the same distribution terms that you use for the rest of that program.
+ the same distribution terms that you use for the rest of that
+ program. This Exception is an additional permission under section 7
+ of the GNU General Public License, version 3 ("GPLv3").
-parse.c:
+[tool/lib/test/*]
+[tool/lib/core_assertions.rb]
- This file is licensed under the GPL, but is incorporated into Ruby and
+ Some of methods on these files are based on MiniTest 4. MiniTest 4 is
+ distributed under the MIT License.
+
+ >>>
+ Copyright (c) Ryan Davis, seattle.rb
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ 'Software'), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+[parse.c]
+[parse.h]
+
+ These files are licensed under the GPL, but are incorporated into Ruby and
redistributed under the terms of the Ruby license, as permitted by the
exception to the GPL below.
- Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
- Free Software Foundation, Inc.
+ >>>
+ Copyright (C) 1984, 1989-1990, 2000-2015, 2018:: Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
- /* As a special exception, you may create a larger work that contains
- part or all of the Bison parser skeleton and distribute that work
- under terms of your choice, so long as that work isn't itself a
- parser generator using the skeleton or a modified version thereof
- as a parser skeleton. Alternatively, if you modify or redistribute
- the parser skeleton itself, you may (at your option) remove this
- special exception, which will cause the skeleton and the resulting
- Bison output files to be licensed under the GNU General Public
- License without this special exception.
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison.
- This special exception was added by the Free Software Foundation in
- version 2.2 of Bison. */
+[missing/dtoa.c]
-util.c (partly):
+ This file is under these licenses.
- Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+ >>>
+ Copyright (c) 1991, 2000, 2001:: by Lucent Technologies.
Permission to use, copy, modify, and distribute this software for any
purpose without fee is hereby granted, provided that this entire notice
@@ -279,71 +425,74 @@ util.c (partly):
REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
-win32/win32.[ch]:
+ >>>
+ Copyright (c) 2004-2008:: David Schultz <das@FreeBSD.ORG>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+[win32/win32.c]
+[include/ruby/win32.h]
You can apply the Artistic License to these files. (or GPL,
alternatively)
- Copyright (c) 1993, Intergraph Corporation
+ >>>
+ Copyright (c) 1993:: Intergraph Corporation
You may distribute under the terms of either the GNU General Public
License or the Artistic License, as specified in the perl README file.
-util.c (partly):
-
- Copyright (c) 2004-2008 David Schultz <das@FreeBSD.ORG>
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
-random.c
+[missing/mt19937.c]
This file is under the new-style BSD license.
- A C-program for MT19937, with initialization improved 2002/2/10.
+ >>>
+ A C-program for MT19937, with initialization improved 2002/2/10.::
Coded by Takuji Nishimura and Makoto Matsumoto.
+
This is a faster version by taking Shawn Cokus's optimization,
Matthe Bellew's simplification, Isaku Wada's real version.
Before using, initialize the state by using init_genrand(seed)
or init_by_array(init_key, key_length).
- Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
- All rights reserved.
+ Copyright (C) 1997 - 2002:: Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
- 3. The names of its contributors may not be used to endorse or promote
- products derived from this software without specific prior written
- permission.
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
@@ -364,12 +513,13 @@ random.c
The Wayback Machine url: http://web.archive.org/web/19990429082237/http://www.math.keio.ac.jp/matumoto/emt.html
-vm_dump.c:procstat_vm
+[missing/procstat_vm.c]
This file is under the new-style BSD license.
- Copyright (c) 2007 Robert N. M. Watson
- All rights reserved.
+ >>>
+ Copyright (c) 2007:: Robert N. M. Watson
+ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -394,146 +544,65 @@ vm_dump.c:procstat_vm
$FreeBSD: head/usr.bin/procstat/procstat_vm.c 261780 2014-02-11 21:57:37Z jhb $
-vsnprintf.c:
+[vsnprintf.c]
- This file is under the old-style BSD license. Note that the
- paragraph 3 below is now null and void.
+ This file is under the {old-style BSD license}[rdoc-label:label-Old-style+BSD+license].
- Copyright (c) 1990, 1993
- The Regents of the University of California. All rights reserved.
+ >>>
+ Copyright (c) 1990, 1993::
+ The Regents of the University of California. All rights reserved.
This code is derived from software contributed to Berkeley by
Chris Torek.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
- IMPORTANT NOTE:
- --------------
- From ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
- paragraph 3 above is now null and void.
-
-st.c:
-strftime.c:
-include/ruby/st.h:
-missing/acosh.c:
-missing/alloca.c:
-missing/dup2.c:
-missing/erf.c:
-missing/finite.c:
-missing/hypot.c:
-missing/isinf.c:
-missing/isnan.c:
-missing/lgamma_r.c:
-missing/memcmp.c:
-missing/memmove.c:
-missing/strchr.c:
-missing/strerror.c:
-missing/strstr.c:
-missing/tgamma.c:
-ext/date/date_strftime.c:
-ext/digest/sha1/sha1.[ch]:
-ext/sdbm/_sdbm.c:
-ext/sdbm/sdbm.h:
+[st.c]
+[strftime.c]
+[include/ruby/st.h]
+[missing/acosh.c]
+[missing/alloca.c]
+[missing/erf.c]
+[missing/hypot.c]
+[missing/lgamma_r.c]
+[missing/memcmp.c]
+[missing/memmove.c]
+[missing/strchr.c]
+[missing/strerror.c]
+[missing/strstr.c]
+[missing/tgamma.c]
+[ext/date/date_strftime.c]
+[ext/digest/sha1/sha1.c]
+[ext/digest/sha1/sha1.h]
These files are all under public domain.
-missing/crypt.c:
+[missing/crypt.c]
- This file is under the old-style BSD license. Note that the
- paragraph 3 below is now null and void.
+ This file is under the {old-style BSD license}[rdoc-label:label-Old-style+BSD+license].
- Copyright (c) 1989, 1993
- The Regents of the University of California. All rights reserved.
+ >>>
+ Copyright (c) 1989, 1993::
+ The Regents of the University of California. All rights reserved.
This code is derived from software contributed to Berkeley by
Tom Truscott.
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
+[missing/setproctitle.c]
- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
+ This file is under the {old-style BSD license}[rdoc-label:label-Old-style+BSD+license].
-missing/setproctitle.c
+ >>>
+ Copyright 2003:: Damien Miller
+ Copyright (c) 1983, 1995-1997:: Eric P. Allman
+ Copyright (c) 1988, 1993::
+ The Regents of the University of California. All rights reserved.
- This file is under the old-style BSD license. Note that the
- paragraph 3 below is now null and void.
-
- Copyright 2003 Damien Miller
- Copyright (c) 1983, 1995-1997 Eric P. Allman
- Copyright (c) 1988, 1993
- The Regents of the University of California. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- 3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
-missing/strlcat.c
-missing/strlcpy.c
+[missing/strlcat.c]
+[missing/strlcpy.c]
These files are under an ISC-style license.
- Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ >>>
+ Copyright (c) 1998, 2015:: Todd C. Miller <Todd.Miller@courtesan.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -547,23 +616,26 @@ missing/strlcpy.c
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-missing/langinfo.c
+[missing/langinfo.c]
This file is from http://www.cl.cam.ac.uk/~mgk25/ucs/langinfo.c.
Ruby uses a modified version. The file contains the following
author/copyright notice:
- Markus.Kuhn@cl.cam.ac.uk -- 2002-03-11
- Permission to use, copy, modify, and distribute this software
- for any purpose and without fee is hereby granted. The author
- disclaims all warranties with regard to this software.
+ >>>
+ Markus.Kuhn@cl.cam.ac.uk -- 2002-03-11::
+ Permission to use, copy, modify, and distribute this software
+ for any purpose and without fee is hereby granted. The author
+ disclaims all warranties with regard to this software.
-ext/digest/md5/md5.[ch]:
+[ext/digest/md5/md5.c]
+[ext/digest/md5/md5.h]
These files are under the following license. Ruby uses modified
versions of them.
- Copyright (C) 1999, 2000 Aladdin Enterprises. All rights reserved.
+ >>>
+ Copyright (C) 1999, 2000:: Aladdin Enterprises. All rights reserved.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -584,23 +656,27 @@ ext/digest/md5/md5.[ch]:
L. Peter Deutsch
ghost@aladdin.com
-ext/digest/rmd160/rmd160.[ch]:
+[ext/digest/rmd160/rmd160.c]
+[ext/digest/rmd160/rmd160.h]
These files have the following copyright information, and by the
author we are allowed to use it under the new-style BSD license.
- AUTHOR: Antoon Bosselaers, ESAT-COSIC
+ >>>
+ AUTHOR:: Antoon Bosselaers, ESAT-COSIC
(Arranged for libc by Todd C. Miller)
- DATE: 1 March 1996
+ DATE:: 1 March 1996
- Copyright (c) Katholieke Universiteit Leuven
+ Copyright (c):: Katholieke Universiteit Leuven
1996, All Rights Reserved
-ext/digest/sha2/sha2.[ch]:
+[ext/digest/sha2/sha2.c]
+[ext/digest/sha2/sha2.h]
These files are under the new-style BSD license.
- Copyright 2000 Aaron D. Gifford. All rights reserved.
+ >>>
+ Copyright 2000:: Aaron D. Gifford. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -626,96 +702,123 @@ ext/digest/sha2/sha2.[ch]:
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
-ext/json/generator/generator.c:
+[ext/json/generator/generator.c]
+
+ The file contains the following copyright notice.
- Copyright 2001-2004 Unicode, Inc.
+ >>>
+ Copyright 2001-2004:: Unicode, Inc.
- Disclaimer
+ Disclaimer::
- This source code is provided as is by Unicode, Inc. No claims are
- made as to fitness for any particular purpose. No warranties of any
- kind are expressed or implied. The recipient agrees to determine
- applicability of information provided. If this file has been
- purchased on magnetic or optical media from Unicode, Inc., the
- sole remedy for any claim will be exchange of defective media
- within 90 days of receipt.
+ This source code is provided as is by Unicode, Inc. No claims are
+ made as to fitness for any particular purpose. No warranties of any
+ kind are expressed or implied. The recipient agrees to determine
+ applicability of information provided. If this file has been
+ purchased on magnetic or optical media from Unicode, Inc., the
+ sole remedy for any claim will be exchange of defective media
+ within 90 days of receipt.
- Limitations on Rights to Redistribute This Code
+ Limitations on Rights to Redistribute This Code::
- Unicode, Inc. hereby grants the right to freely use the information
- supplied in this file in the creation of products supporting the
- Unicode Standard, and to make copies of this file in any form
- for internal or external distribution as long as this notice
- remains attached.
+ Unicode, Inc. hereby grants the right to freely use the information
+ supplied in this file in the creation of products supporting the
+ Unicode Standard, and to make copies of this file in any form
+ for internal or external distribution as long as this notice
+ remains attached.
-ext/nkf/nkf-utf8/config.h:
-ext/nkf/nkf-utf8/nkf.c:
-ext/nkf/nkf-utf8/utf8tbl.c:
+[ext/nkf/nkf-utf8/config.h]
+[ext/nkf/nkf-utf8/nkf.c]
+[ext/nkf/nkf-utf8/utf8tbl.c]
These files are under the following license. So to speak, it is
copyrighted semi-public-domain software.
- Copyright (C) 1987, Fujitsu LTD. (Itaru ICHIKAWA)
- Everyone is permitted to do anything on this program
- including copying, modifying, improving,
- as long as you don't try to pretend that you wrote it.
- i.e., the above copyright notice has to appear in all copies.
- Binary distribution requires original version messages.
- You don't have to ask before copying, redistribution or publishing.
- THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE.
-
-ext/psych:
-test/psych:
-
- Copyright 2009 Aaron Patterson, et al.
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the 'Software'), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- of the Software, and to permit persons to whom the Software is furnished to do
- so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-
-ext/psych/yaml:
-
- Copyright (c) 2006 Kirill Simonov
-
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- of the Software, and to permit persons to whom the Software is furnished to do
- so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-
-ext/socket/addrinfo.h:
-ext/socket/getaddrinfo.c:
-ext/socket/getnameinfo.c:
+ >>>
+ Copyright (C) 1987:: Fujitsu LTD. (Itaru ICHIKAWA)
+
+ Everyone is permitted to do anything on this program
+ including copying, modifying, improving,
+ as long as you don't try to pretend that you wrote it.
+ i.e., the above copyright notice has to appear in all copies.
+ Binary distribution requires original version messages.
+ You don't have to ask before copying, redistribution or publishing.
+ THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE.
+
+[ext/psych]
+[test/psych]
+
+ The files under these directories are under the following license, except for
+ ext/psych/yaml.
+
+ >>>
+ Copyright 2009:: Aaron Patterson, et al.
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the 'Software'), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+[ext/psych/yaml]
+
+ The files under this directory are under the following license.
+
+ >>>
+ Copyright (c) 2006:: Kirill Simonov
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
+ this software and associated documentation files (the "Software"), to deal in
+ the Software without restriction, including without limitation the rights to
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is furnished to do
+ so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+[ext/pty/pty.c]
+
+ >>>
+ C) Copyright 1998:: by Akinori Ito.
+
+ This software may be redistributed freely for this purpose, in full
+ or in part, provided that this entire copyright notice is included
+ on any copies of this software and applications and derivations thereof.
+
+ This software is provided on an "as is" basis, without warranty of any
+ kind, either expressed or implied, as to any matter including, but not
+ limited to warranty of fitness of purpose, or merchantability, or
+ results obtained from use of this software.
+
+[ext/socket/addrinfo.h]
+[ext/socket/getaddrinfo.c]
+[ext/socket/getnameinfo.c]
These files are under the new-style BSD license.
- Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ >>>
+ Copyright (C) 1995, 1996, 1997, 1998, and 1999:: WIDE Project.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -742,15 +845,16 @@ ext/socket/getnameinfo.c:
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
-ext/win32ole/win32ole.c:
+[ext/win32ole/win32ole.c]
You can apply the Artistic License to this file. (or GPL,
alternatively)
- (c) 1995 Microsoft Corporation. All rights reserved.
+ >>>
+ (c) 1995:: Microsoft Corporation. All rights reserved.
Developed by ActiveWare Internet Corp., http://www.ActiveWare.com
- Other modifications Copyright (c) 1997, 1998 by Gurusamy Sarathy
+ Other modifications Copyright (c) 1997, 1998:: by Gurusamy Sarathy
<gsar@umich.edu> and Jan Dubois <jan.dubois@ibm.net>
You may distribute under the terms of either the GNU General Public
@@ -759,16 +863,17 @@ ext/win32ole/win32ole.c:
The Wayback Machine url: http://web.archive.org/web/19970607104352/http://www.activeware.com:80/
-lib/rdoc/generator/template/darkfish/css/fonts.css:
+[lib/rdoc/generator/template/darkfish/css/fonts.css]
- This file is licensed under the SIL Open Font License.
+ This file is licensed under the {SIL Open Font License}[http://scripts.sil.org/OFL].
- http://scripts.sil.org/OFL
+[spec/mspec]
+[spec/ruby]
-spec/mspec:
-spec/ruby:
+ The files under these directories are under the following license.
- Copyright (c) 2008 Engine Yard, Inc. All rights reserved.
+ >>>
+ Copyright (c) 2008:: Engine Yard, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
@@ -791,60 +896,224 @@ spec/ruby:
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
-lib/rubygems.rb:
-lib/rubygems:
-test/rubygems:
+[lib/rubygems.rb]
+[lib/rubygems]
+[test/rubygems]
+
+ RubyGems is under the following license.
+
+ >>>
+ RubyGems is copyrighted free software by Chad Fowler, Rich Kilmer, Jim
+ Weirich and others. You can redistribute it and/or modify it under
+ either the terms of the {MIT license}[rdoc-label:label-MIT+License], or the conditions
+ below:
- RubyGems is copyrighted free software by Chad Fowler, Rich Kilmer, Jim
- Weirich and others. You can redistribute it and/or modify it under
- either the terms of the MIT license (see the file MIT.txt), or the
- conditions below:
+ 1. You may make and give away verbatim copies of the source form of the
+ software without restriction, provided that you duplicate all of the
+ original copyright notices and associated disclaimers.
- 1. You may make and give away verbatim copies of the source form of the
- software without restriction, provided that you duplicate all of the
- original copyright notices and associated disclaimers.
+ 2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
- 2. You may modify your copy of the software in any way, provided that
- you do at least ONE of the following:
+ a. place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
- a. place your modifications in the Public Domain or otherwise
- make them Freely Available, such as by posting said
- modifications to Usenet or an equivalent medium, or by allowing
- the author to include your modifications in the software.
+ b. use the modified software only within your corporation or
+ organization.
- b. use the modified software only within your corporation or
- organization.
+ c. give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
- c. give non-standard executables non-standard names, with
- instructions on where to get the original software distribution.
+ d. make other distribution arrangements with the author.
- d. make other distribution arrangements with the author.
+ 3. You may distribute the software in object code or executable
+ form, provided that you do at least ONE of the following:
- 3. You may distribute the software in object code or executable
- form, provided that you do at least ONE of the following:
+ a. distribute the executables and library files of the software,
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
- a. distribute the executables and library files of the software,
- together with instructions (in the manual page or equivalent)
- on where to get the original distribution.
+ b. accompany the distribution with the machine-readable source of
+ the software.
- b. accompany the distribution with the machine-readable source of
- the software.
+ c. give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
- c. give non-standard executables non-standard names, with
- instructions on where to get the original software distribution.
+ d. make other distribution arrangements with the author.
- d . make other distribution arrangements with the author.
+ 4. You may modify and include the part of the software into any other
+ software (possibly commercial).
- 4. You may modify and include the part of the software into any other
- software (possibly commercial).
+ 5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whomever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
- 5. The scripts and library files supplied as input to or produced as
- output from the software do not automatically fall under the
- copyright of the software, but belong to whomever generated them,
- and may be sold commercially, and may be aggregated with this
- software.
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
- 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
- IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE.
+[lib/bundler]
+[lib/bundler.rb]
+[spec/bundler]
+
+ Bundler is under the following license.
+
+ >>>
+ Portions copyright (c) 2010:: Andre Arko
+ Portions copyright (c) 2009:: Engine Yard
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
+[lib/bundler/vendor/thor]
+
+ Thor is under the following license.
+
+ >>>
+ Copyright (c) 2008 Yehuda Katz, Eric Hodel, et al.
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
+[lib/rubygems/resolver/molinillo]
+
+ molinillo is under the following license.
+
+ >>>
+ Copyright (c) 2014 Samuel E. Giddins segiddins@segiddins.me
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
+[lib/bundler/vendor/pub_grub]
+
+ pub_grub is under the following license.
+
+ >>>
+ Copyright (c) 2018 John Hawthorn
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
+[lib/bundler/vendor/connection_pool]
+
+ connection_pool is under the following license.
+
+ >>>
+ Copyright (c) 2011 Mike Perham
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
+[lib/bundler/vendor/net-http-persistent]
+
+ net-http-persistent is under the following license.
+
+ >>>
+ Copyright (c) Eric Hodel, Aaron Patterson
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
+[lib/did_you_mean]
+[lib/did_you_mean.rb]
+[test/did_you_mean]
+
+ did_you_mean is under the following license.
+
+ >>>
+ Copyright (c) 2014-2016 Yuki Nishijima
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
+[lib/error_highlight]
+[lib/error_highlight.rb]
+[test/error_highlight]
+
+ error_highlight is under the following license.
+
+ >>>
+ Copyright (c) 2021 Yusuke Endoh
+
+ {MIT License}[rdoc-label:label-MIT+License]
+
+[benchmark/so_ackermann.rb]
+[benchmark/so_array.rb]
+[benchmark/so_binary_trees.rb]
+[benchmark/so_concatenate.rb]
+[benchmark/so_count_words.yml]
+[benchmark/so_exception.rb]
+[benchmark/so_fannkuch.rb]
+[benchmark/so_fasta.rb]
+[benchmark/so_k_nucleotide.yml]
+[benchmark/so_lists.rb]
+[benchmark/so_mandelbrot.rb]
+[benchmark/so_matrix.rb]
+[benchmark/so_meteor_contest.rb]
+[benchmark/so_nbody.rb]
+[benchmark/so_nested_loop.rb]
+[benchmark/so_nsieve.rb]
+[benchmark/so_nsieve_bits.rb]
+[benchmark/so_object.rb]
+[benchmark/so_partial_sums.rb]
+[benchmark/so_pidigits.rb]
+[benchmark/so_random.rb]
+[benchmark/so_reverse_complement.yml]
+[benchmark/so_sieve.rb]
+[benchmark/so_spectralnorm.rb]
+
+ These files are very old copy of then-called "The Great Computer Language
+ Shootout". LEGAL SITUATION OF THESE FILES ARE UNCLEAR because the original
+ site has been lost. Upstream diverged to delete several benchmarks listed
+ above.
+
+== MIT License
+>>>
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+== Old-style BSD license
+>>>
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ IMPORTANT NOTE::
+
+ From ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ paragraph 3 above is now null and void.
diff --git a/Makefile.in b/Makefile.in
deleted file mode 100644
index bdfe3ab6c9..0000000000
--- a/Makefile.in
+++ /dev/null
@@ -1,628 +0,0 @@
-SHELL = /bin/sh
-NULLCMD = @NULLCMD@
-n=$(NULLCMD)
-ECHO1 = $(V:1=$n)
-RUNCMD = $(SHELL)
-CDPATH = .
-CHDIR = @CHDIR@
-exec = exec
-NULL = /dev/null
-PATH_SEPARATOR = @PATH_SEPARATOR@
-
-#### Start of system configuration section. ####
-
-srcdir = @srcdir@
-top_srcdir = $(srcdir)
-hdrdir = $(srcdir)/include
-PLATFORM_DIR = @PLATFORM_DIR@
-
-CC = @CC@
-CPP = @CPP@
-LD = @LD@
-YACC = bison
-PURIFY =
-AUTOCONF = autoconf
-ACLOCAL = aclocal
-CONFIGURE = @CONFIGURE@
-@SET_MAKE@
-MKFILES = @MAKEFILES@
-BASERUBY = @BASERUBY@
-HAVE_BASERUBY = @HAVE_BASERUBY@
-TEST_RUNNABLE = @TEST_RUNNABLE@
-CROSS_COMPILING = @CROSS_COMPILING@
-DOXYGEN = @DOXYGEN@
-
-prefix = @prefix@
-exec_prefix = @exec_prefix@
-bindir = @bindir@
-sbindir = @sbindir@
-libdir = @libdir@
-libexecdir = @libexecdir@
-datarootdir = @datarootdir@
-datadir = @datadir@
-arch = @arch@
-sitearch = @sitearch@
-sitedir = @sitedir@
-archlibdir = @archlibdir@
-ruby_version = @ruby_version@
-RUBY_VERSION_NAME = @RUBY_VERSION_NAME@
-UNIVERSAL_ARCHNAMES = @UNIVERSAL_ARCHNAMES@
-
-TESTUI = console
-TESTS =
-INSTALLDOC = @INSTALLDOC@
-DOCTARGETS = @RDOCTARGET@ @CAPITARGET@
-
-EXTOUT = @EXTOUT@
-TIMESTAMPDIR = $(EXTOUT)/.timestamp
-arch_hdrdir = $(EXTOUT)/include/$(arch)
-VPATH = $(arch_hdrdir)/ruby:$(hdrdir)/ruby:$(srcdir):$(srcdir)/missing
-
-empty =
-CC_VERSION = @CC_VERSION@
-OUTFLAG = @OUTFLAG@$(empty)
-COUTFLAG = @COUTFLAG@$(empty)
-CPPOUTFLAG = $(COUTFLAG)
-ARCH_FLAG = @ARCH_FLAG@
-CFLAGS_NO_ARCH = @CFLAGS@
-CFLAGS = $(CFLAGS_NO_ARCH) $(ARCH_FLAG)
-cflags = @cflags@
-optflags = @optflags@
-debugflags = @debugflags@
-warnflags = @warnflags@ @strict_warnflags@
-cppflags = @cppflags@
-MATHN = @MATHN@
-XCFLAGS = @XCFLAGS@ $(MATHN:yes=-DCANONICALIZATION_FOR_MATHN)
-USE_RUBYGEMS = @USE_RUBYGEMS@
-USE_RUBYGEMS_ = $(USE_RUBYGEMS:yes=)
-CPPFLAGS = @CPPFLAGS@ $(INCFLAGS) $(USE_RUBYGEMS_:no=-DDISABLE_RUBYGEMS=1)
-MJIT_HEADER_FLAGS = @MJIT_HEADER_FLAGS@
-MJIT_HEADER_SUFFIX =
-MJIT_HEADER_ARCH =
-MJIT_HEADER_INSTALL_DIR = @MJIT_HEADER_INSTALL_DIR@
-MJIT_CC = @MJIT_CC@
-MJIT_OPTFLAGS = @MJIT_OPTFLAGS@
-MJIT_DEBUGFLAGS = @MJIT_DEBUGFLAGS@
-MJIT_LDSHARED = @MJIT_LDSHARED@
-MJIT_DLDFLAGS = @DLDFLAGS@
-MJIT_HEADER = rb_mjit_header.h
-MJIT_MIN_HEADER_NAME = rb_mjit_min_header-$(RUBY_PROGRAM_VERSION).h
-MJIT_MIN_HEADER = $(MJIT_HEADER_BUILD_DIR)/$(MJIT_MIN_HEADER_NAME)
-MJIT_HEADER_BUILD_DIR = $(EXTOUT)/include/$(arch)
-LDFLAGS = @STATIC@ $(CFLAGS) @LDFLAGS@
-EXTLDFLAGS = @EXTLDFLAGS@
-XLDFLAGS = @XLDFLAGS@ $(EXTLDFLAGS)
-EXTLIBS =
-LIBS = @LIBS@ $(EXTLIBS)
-MISSING = @LIBOBJS@ @ALLOCA@
-ENABLE_SHARED = @ENABLE_SHARED@
-LDSHARED = @LIBRUBY_LDSHARED@
-DLDFLAGS = @LIBRUBY_DLDFLAGS@ $(XLDFLAGS) $(ARCH_FLAG)
-SOLIBS = @SOLIBS@
-ENABLE_DEBUG_ENV = @ENABLE_DEBUG_ENV@
-MAINLIBS = @MAINLIBS@
-ARCHMINIOBJS = @MINIOBJS@
-DLNOBJ = @DLNOBJ@
-ENCOBJS = @ENCOBJS@
-EXTOBJS = @EXTOBJS@
-BUILTIN_ENCOBJS = @BUILTIN_ENCOBJS@
-BUILTIN_TRANSSRCS = @BUILTIN_TRANSSRCS@
-BUILTIN_TRANSOBJS = @BUILTIN_TRANSOBJS@
-POSTLINK = @POSTLINK@
-
-RUBY_BASE_NAME=@RUBY_BASE_NAME@
-RUBY_PROGRAM_VERSION=@RUBY_PROGRAM_VERSION@
-RUBY_API_VERSION=@RUBY_API_VERSION@
-RUBY_INSTALL_NAME=@RUBY_INSTALL_NAME@
-RUBY_SO_NAME=@RUBY_SO_NAME@
-EXEEXT = @EXEEXT@
-LIBEXT = @LIBEXT@
-PROGRAM=$(RUBY_INSTALL_NAME)$(EXEEXT)
-RUBY = $(RUBY_INSTALL_NAME)
-MINIRUBY = @MINIRUBY@\
- $(MINIRUBYOPT)
-# RUNRUBY_COMMAND:: runruby.rb or baseruby. do not append options directly
-RUNRUBY_COMMAND = @RUNRUBY_COMMAND@
-# RUNRUBY:: run ruby with RUN_OPTS which is passed to ruby
-RUNRUBY = @RUNRUBY@ $(RUN_OPTS)
-# RUNRUBY_DEBUGGER:: debugging option for runruby.rb
-RUNRUBY_DEBUGGER = --debugger='gdb -x run.gdb --quiet --args'
-XRUBY = @XRUBY@
-BTESTRUBY = @BTESTRUBY@\
- $(MINIRUBYOPT)
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-XRUBY_LIBDIR = @XRUBY_LIBDIR@
-XRUBY_RUBYLIBDIR = @XRUBY_RUBYLIBDIR@
-XRUBY_RUBYHDRDIR = @XRUBY_RUBYHDRDIR@
-BOOTSTRAPRUBY = @BOOTSTRAPRUBY@
-
-#### End of system configuration section. ####
-
-MAJOR= @MAJOR@
-MINOR= @MINOR@
-TEENY= @TEENY@
-RUBY_PROGRAM_VERSION = @RUBY_PROGRAM_VERSION@
-
-LIBRUBY_A = @LIBRUBY_A@
-LIBRUBY_SO = @LIBRUBY_SO@
-LIBRUBY_SONAME= @LIBRUBY_SONAME@
-LIBRUBY_ALIASES= @LIBRUBY_ALIASES@
-LIBRUBY = @LIBRUBY@
-LIBRUBYARG = @LIBRUBYARG@
-LIBRUBYARG_STATIC = @LIBRUBYARG_STATIC@
-LIBRUBYARG_SHARED = @LIBRUBYARG_SHARED@
-LIBRUBY_RELATIVE = @LIBRUBY_RELATIVE@
-LIBRUBY_A_OBJS = @LIBRUBY_A_OBJS@
-
-DTRACE_REBUILD_OBJS = $(DTRACE_REBUILD:yes=$(DTRACE_DEPENDENT_OBJS))
-
-DTRACE_DEPENDENT_OBJS = array.$(OBJEXT) \
- eval.$(OBJEXT) \
- gc.$(OBJEXT) \
- hash.$(OBJEXT) \
- load.$(OBJEXT) \
- object.$(OBJEXT) \
- parse.$(OBJEXT) \
- string.$(OBJEXT) \
- symbol.$(OBJEXT) \
- vm.$(OBJEXT)
-
-THREAD_MODEL = @THREAD_MODEL@
-
-PREP = @PREP@
-ARCHFILE = @ARCHFILE@
-SETUP =
-EXTSTATIC = @EXTSTATIC@
-ENCSTATIC = @ENCSTATIC@
-SET_LC_MESSAGES = env LC_MESSAGES=C
-
-MAKEDIRS = @MKDIR_P@
-CP = cp
-MV = mv
-RM = rm -f
-RMDIR = @RMDIR@
-RMDIRS = @RMDIRS@
-RMALL = @RMALL@
-NM = @NM@
-AR = @AR@
-ARFLAGS = @ARFLAGS@$(empty)
-RANLIB = @RANLIB@
-AS = @AS@
-ASFLAGS = @ASFLAGS@ $(INCFLAGS)
-IFCHANGE = $(srcdir)/tool/ifchange
-OBJDUMP = @OBJDUMP@
-OBJCOPY = @OBJCOPY@
-HAVE_GIT = @HAVE_GIT@
-GIT = @GIT@
-VCS = @VCS@
-VCSUP = @VCSUP@
-DTRACE = @DTRACE@ @DTRACE_OPT@
-DTRACE_EXT = @DTRACE_EXT@
-DTRACE_OBJ = @DTRACE_OBJ@
-DTRACE_REBUILD= @DTRACE_REBUILD@
-DTRACE_GLOMMED_OBJ = $(DTRACE_REBUILD:yes=ruby-glommed.$(OBJEXT))
-
-OBJEXT = @OBJEXT@
-ASMEXT = S
-SOEXT = @SOEXT@
-DLEXT = @DLEXT@
-MANTYPE = @MANTYPE@
-SYMBOL_PREFIX = @SYMBOL_PREFIX@
-
-INSTALLED_LIST= .installed.list
-
-NEWLINE_C = enc/trans/newline.c
-MINIPRELUDE_C = miniprelude.c
-PRELUDE_C = prelude.c
-GOLF_PRELUDE_C= golf_prelude.c
-RBCONFIG = .rbconfig.time
-
-MAINSRC = $(MAINOBJ:@OBJEXT@=c)
-
-SRC_FILE = $<
-OS_SRC_FILE = $<
-DEST_FILE = $@
-OS_DEST_FILE = $@
-
-MESSAGE_BEGIN = @for line in
-MESSAGE_END = ; do echo "$$line"; done
-ECHO_BEGIN = @sep=''; for word in
-ECHO_END = ; do echo @ECHO_N@ "$$sep'$$word'@ECHO_C@"; sep=' '; done; echo
-
-DESTDIR = @DESTDIR@
-
-configure_args = @configure_args@
-#### End of variables
-
-.SUFFIXES: .inc .h .c .y .i .$(DTRACE_EXT)
-
-all:
-
-# Prevent GNU make v3 from overflowing arg limit on SysV.
-.NOEXPORT:
-
-miniruby$(EXEEXT):
- @-if test -f $@; then $(MV) -f $@ $@.old; $(RM) $@.old; fi
- $(ECHO) linking $@
- $(Q) $(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(NORMALMAINOBJ) $(MINIOBJS) $(COMMONOBJS) $(MAINLIBS) $(LIBS) $(OUTFLAG)$@
- $(Q) $(POSTLINK)
-
-$(PROGRAM):
- @$(RM) $@
- $(ECHO) linking $@
- $(Q) $(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(MAINLIBS) $(LIBS) $(EXTLIBS) $(OUTFLAG)$@
- $(Q) $(POSTLINK)
-
-PRE_LIBRUBY_UPDATE = [ -n "$(LIBRUBY_SO_UPDATE)" ] || $(exec) $(RM) $(LIBRUBY_EXTS)
-
-# We must `rm' the library each time this rule is invoked because "updating" a
-# MAB library on Apple/NeXT (see --enable-fat-binary in configure) is not
-# supported.
-$(LIBRUBY_A):
- @$(RM) $@
- @-[ -z "$(EXTSTATIC)" ] || $(PRE_LIBRUBY_UPDATE)
- $(ECHO) linking static-library $@
- $(Q) $(AR) $(ARFLAGS) $@ $(LIBRUBY_A_OBJS) $(INITOBJS)
- @-$(RANLIB) $@ 2> /dev/null || true
-
-verify-static-library: $(LIBRUBY_A)
- $(ECHO) verifying static-library $@
- @$(PURIFY) $(CC) $(LDFLAGS) $(XLDFLAGS) $(MAINOBJ) $(LIBRUBY_A) $(MAINLIBS) $(EXTLIBS) $(LIBS) $(OUTFLAG)conftest$(EXEEXT)
- @$(RMALL) conftest$(EXEEXT) conftest.c conftest.dSYM
-
-$(LIBRUBY_SO):
- @-[ -n "$(EXTSTATIC)" ] || $(PRE_LIBRUBY_UPDATE)
- $(ECHO) linking shared-library $@
- $(Q) $(LDSHARED) $(DLDFLAGS) $(OBJS) $(DLDOBJS) $(SOLIBS) $(EXTSOLIBS) $(OUTFLAG)$@
- -$(Q) $(OBJCOPY) -w -L '$(SYMBOL_PREFIX)Init_*' -L '$(SYMBOL_PREFIX)ruby_static_id_*' \
- -L '$(SYMBOL_PREFIX)*_threadptr_*' -L '$(SYMBOL_PREFIX)*_ec_*' $@
- $(Q) $(POSTLINK)
- @-$(MINIRUBY) -e 'so, *aliases = ARGV; aliases.uniq!; aliases.delete(File.basename(so)); \
- aliases.each { |link| File.delete link rescue nil; File.symlink so, link }' \
- $(LIBRUBY_SO) $(LIBRUBY_ALIASES) || true
-
-LIBRUBY_WITH_EXT = @LIBRUBY_WITH_EXT@
-$(LIBRUBY_$(LIBRUBY_WITH_EXT)): $(LIBRUBY_SO_UPDATE)
-
-ruby_pc = @ruby_pc@
-ruby.pc: $(ruby_pc)
-$(ruby_pc): config.status
- @./config.status --file=$@:$(srcdir)/template/ruby.pc.in
-
-ruby-runner.h: template/ruby-runner.h.in config.status
- @./config.status --file=$@:$(srcdir)/template/$(@F).in
-
-$(RBCONFIG): $(PREP)
-
-rbconfig.rb: $(RBCONFIG)
-
-install-cross: $(arch)-fake.rb $(RBCONFIG) rbconfig.rb $(arch_hdrdir)/ruby/config.h \
- $(LIBRUBY_A) $(LIBRUBY_SO) $(ARCHFILE)
- $(ECHO) installing cross-compiling stuff
- $(Q) $(MAKEDIRS) $(XRUBY_RUBYLIBDIR)/$(arch) $(XRUBY_RUBYHDRDIR)/$(arch)/ruby
- $(Q) sed '/^\$$:\.unshift/q' $(arch)-fake.rb > fake.rb
- $(Q) $(BASERUBY) -p \
- -e '~/^\s*CONFIG\["LDFLAGS"\]/ and' \
- -e '$$_[/(?=\s*"$$)/] = %q[ #{(CONFIG["LIBPATHFLAG"]%File.dirname(__FILE__)).strip}]' \
- rbconfig.rb > fake-rbconfig.rb
- $(INSTALL_SCRIPT) fake.rb $(XRUBY_RUBYLIBDIR)/$(arch)/fake.rb
- $(INSTALL_SCRIPT) fake-rbconfig.rb $(XRUBY_RUBYLIBDIR)/$(arch)/rbconfig.rb
- @$(RM) fake.rb fake-rbconfig.rb
- $(INSTALL_DATA) $(arch_hdrdir)/ruby/config.h $(XRUBY_RUBYHDRDIR)/$(arch)/ruby
- $(INSTALL_DATA) $(top_srcdir)/include/ruby/win32.h $(XRUBY_RUBYHDRDIR)/ruby
- $(INSTALL_DATA) $(LIBRUBY) $(LIBRUBY_A) $(XRUBY_RUBYLIBDIR)/$(arch)
- $(INSTALL_PROGRAM) $(LIBRUBY_SO) $(XRUBY_RUBYLIBDIR)/$(arch)
-
-Makefile: $(srcdir)/Makefile.in $(srcdir)/enc/Makefile.in
-
-$(MKFILES): config.status $(srcdir)/version.h
- @[ -f $@ ] && mv $@ $@.old
- MAKE=$(MAKE) $(SHELL) ./config.status $@
- @cmp $@ $@.old > /dev/null 2>&1 && echo $@ unchanged && exit 0; \
- { \
- echo "all:; -@rm -f conftest.mk"; \
- echo "conftest.mk: .force; @echo AUTO_REMAKE"; \
- echo ".force:"; \
- } > conftest.mk || exit 1; \
- $(MAKE) -f conftest.mk | grep '^AUTO_REMAKE$$' >/dev/null 2>&1 || \
- { echo "$@ updated, restart."; exit 1; }
-
-uncommon.mk: $(srcdir)/common.mk
- sed 's/{\$$([^(){}]*)[^{}]*}//g' $< > $@
-
-.PHONY: reconfig
-reconfig-args = $(srcdir)/$(CONFIGURE) $(configure_args)
-config.status-args = ./config.status --recheck
-reconfig-exec-0 = test -t 1 && { CONFIGURE_TTY=yes; export CONFIGURE_TTY; }; exec 3>&1; exit `exec 4>&1; { "$$@" 3>&- 4>&-; echo $$? 1>&4; } | fgrep -v '(cached)' 1>&3 3>&- 4>&-`
-reconfig-exec-1 = set -x; "$$@"
-
-reconfig config.status: $(srcdir)/$(CONFIGURE) $(srcdir)/enc/Makefile.in \
- $(srcdir)/include/ruby/version.h
- @PWD= MINIRUBY="$(MINIRUBY)"; export MINIRUBY; \
- set $(SHELL) $($@-args); $(reconfig-exec-$(V))
-
-$(srcdir)/$(CONFIGURE): $(srcdir)/configure.ac $(srcdir)/aclocal.m4
- $(CHDIR) $(srcdir) && exec $(AUTOCONF) -o $(@F)
-
-$(srcdir)/aclocal.m4:
- $(CHDIR) $(srcdir) && \
- type $(ACLOCAL) >/dev/null 2>&1 && exec $(ACLOCAL); \
- touch $(@F)
-
-prereq: $(srcdir)/$(CONFIGURE)
-
-incs: id.h
-all-incs: probes.h
-
-# Things which should be considered:
-# * with gperf v.s. without gperf
-# * committers may have various versions of gperf
-# * ./configure v.s. ../ruby/configure
-# * GNU make v.s. HP-UX make # HP-UX make invokes the action if lex.c and keywords has same mtime.
-# * svn checkout generate a file with mtime as current time
-# * ext4 and XFS has a mtime with fractional part
-lex.c: defs/keywords
- @\
- if cmp -s $(srcdir)/defs/lex.c.src $?; then \
- [ $(Q) ] && echo copying $@ || set -x; \
- $(CP) $(srcdir)/lex.c.blt $@; \
- else \
- [ $(Q) ] && echo generating $@ || set -x; \
- gperf -C -P -p -j1 -i 1 -g -o -t -N rb_reserved_word -k1,3,$$ $? \
- | sed -f $(srcdir)/tool/gperf.sed \
- > $@.tmp && \
- $(MV) $@.tmp $@ && \
- $(CP) $? $(srcdir)/defs/lex.c.src && \
- $(CP) $@ $(srcdir)/lex.c.blt; \
- fi
-
-JIS_PROPS_OPTIONS = -k1,3 -7 -c -j1 -i1 -t -C -P -t --ignore-case -H onig_jis_property_hash -Q onig_jis_property_pool -N onig_jis_property
-
-$(srcdir)/enc/jis/props.h: enc/jis/props.kwd
- $(MAKEDIRS) $(@D)
- @set +e; \
- if cmp -s $(?:.kwd=.src) $?; then \
- set -x; \
- $(CP) $(?:.kwd=.h.blt) $@; \
- else \
- set -x; \
- gperf $(JIS_PROPS_OPTIONS) $? | \
- sed -f $(srcdir)/tool/gperf.sed > $@ && \
- $(CP) $? $(?:.kwd=.src) && \
- $(CP) $@ $(?:.kwd=.h.blt); \
- fi
-
-.c.@OBJEXT@:
- @$(ECHO) compiling $<
- $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $<
-
-.s.@OBJEXT@:
- @$(ECHO) assembling $<
- $(Q) $(AS) $(ASFLAGS) -o $@ $<
-
-.c.S:
- @$(ECHO) translating $<
- $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -S $<
-
-.c.i:
- @$(ECHO) preprocessing $<
- $(Q) $(CPP) $(warnflags) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -E $< > $@
-
-.d.h:
- @$(ECHO) translating probes $<
- $(Q) $(DTRACE) -o $@.tmp -h -C $(INCFLAGS) -s $<
- $(Q) sed -e 's/RUBY_/RUBY_DTRACE_/g' -e 's/PROBES_H_TMP/RUBY_PROBES_H/' -e 's/(char \*/(const char */g' -e 's/, char \*/, const char */g' $@.tmp > $@
- $(Q) $(RM) $@.tmp
-
-.dmyh.h:
- @$(ECHO) making dummy $(DEST_FILE)
- $(Q)echo '#include "$(*F).dmyh"' > $@
-
-probes.stamp: $(DTRACE_REBUILD_OBJS)
- $(Q) if test -f $@ -o -f probes.$(OBJEXT); then \
- $(RM) $(DTRACE_REBUILD_OBJS) $@; \
- $(ECHO0) "rebuilding objects which were modified by \"dtrace -G\""; \
- $(MAKE) $(DTRACE_REBUILD_OBJS); \
- fi
- $(Q) touch $@
-
-probes.$(OBJEXT): $(srcdir)/probes.d $(DTRACE_REBUILD:yes=probes.stamp)
- @$(ECHO) processing probes in object files
- $(Q) $(RM) $@
- $(Q) $(DTRACE) -G -C $(INCFLAGS) -s $(srcdir)/probes.d -o $@ $(DTRACE_REBUILD_OBJS)
-
-main: $(MJIT_MIN_HEADER)
-mjit-headers: $(MJIT_MIN_HEADER)
-clean-local::
- $(Q)$(RM) $(MJIT_HEADER) $(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX:%=*).h
- $(Q)$(RM) $(MJIT_MIN_HEADER) $(MJIT_MIN_HEADER:.h=)$(MJIT_HEADER_SUFFIX:%=*).h
- $(Q)$(RM) $(MJIT_HEADER_INSTALL_DIR)/rb_mjit_min_header-*.h
- $(Q)$(RM) $(TIMESTAMPDIR)/$(MJIT_HEADER:.h=)$(MJIT_HEADER_SUFFIX).time mjit_config.h
- -$(Q) $(RMDIRS) $(MJIT_HEADER_INSTALL_DIR) 2> $(NULL) || exit 0
-
-# DTrace static library hacks described here:
-# http://mail.opensolaris.org/pipermail/dtrace-discuss/2005-August/000207.html
-ruby-glommed.$(OBJEXT):
- @$(ECHO) generating a glommed object with DTrace probes for static library
- $(Q) $(LD) -r -o $@ $(OBJS)
-
-clean-local::
- $(Q)$(RM) ext/extinit.c ext/extinit.$(OBJEXT) ext/ripper/y.output \
- enc/encinit.c enc/encinit.$(OBJEXT)
- -$(Q)$(RM) $(pkgconfig_DATA)
- -$(Q)$(RMALL) exe/ ruby-runner.h *.dSYM
-
-distclean-local::
- $(Q)$(RM) ext/config.cache $(RBCONFIG) Doxyfile
- -$(Q)$(RM) run.gdb
- -$(Q)$(RM) $(INSTALLED_LIST) $(arch_hdrdir)/ruby/config.h verconf.h
- -$(Q)$(RMDIRS) $(arch_hdrdir)/ruby 2> /dev/null || true
-
-ext/clean.sub gems/clean.sub:: ext/clean.mk
-ext/distclean.sub gems/distclean.sub:: ext/distclean.mk
-ext/realclean.sub gems/realclean.sub:: ext/realclean.mk
-
-ext/clean.mk ext/distclean.mk ext/realclean.mk::
- -$(Q) if [ -f $(EXTS_MK) ]; then exec $(MAKE) -f $(EXTS_MK) $(@F:.mk=); fi
-
-ext/clean:: ext/clean.sub
-ext/distclean:: ext/distclean.sub
-ext/realclean:: ext/realclean.sub
-gems/clean:: gems/clean.sub
-gems/distclean:: gems/distclean.sub
-gems/realclean:: gems/realclean.sub
-
-ext/clean.sub ext/distclean.sub ext/realclean.sub \
-gems/clean.sub gems/distclean.sub gems/realclean.sub::
- $(Q) set dummy `echo "${EXTS}" | tr , ' '`; shift; \
- test "$$#" = 0 && set .; \
- set dummy `\
- cd $(@D) 2>/dev/null && \
- find "$$@" \( -name Makefile -o -name exts.mk \) -print | \
- sed -n 's:^\./::;s:^:$(@D)/:;s:/[^/][^/]*$$::p' | sort -u; \
- `; shift; \
- for dir do \
- $(RM) "$$dir/exts.mk"; \
- if [ -f "$$dir/Makefile" ]; then \
- echo $(@F:.sub=)ing "$$dir"; \
- (cd "$$dir" && exec $(MAKE) $(mflags) $(@F:.sub=)); \
- fi; \
- done || true
-
-ext/distclean ext/realclean gems/distclean gems/realclean::
- $(Q) set dummy `echo "${EXTS}" | tr , ' '`; shift; \
- test "$$#" = 0 && set .; \
- cd $(@D) 2>/dev/null && \
- find "$$@" -type d -empty -exec $(RMDIRS) {} + 2> /dev/null || true
- $(Q) $(RMDIRS) $(@D) 2> /dev/null || true
-
-clean-enc distclean-enc realclean-enc:
- @test -f "$(ENC_MK)" || exit 0; \
- echo $(@:-enc=ing) encodings; \
- exec $(MAKE) $(MAKE_ENC) $(@:-enc=)
-
-ext/extinit.$(OBJEXT): ext/extinit.c $(SETUP)
- $(ECHO) compiling $@
- $(Q) $(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c ext/extinit.c
-
-enc/encinit.$(OBJEXT): enc/encinit.c $(SETUP)
-
-test-bundled-gems-run:
- $(Q) set -e; while read gem _; do \
- echo testing $$gem gem && \
- $(XRUBY) -C $(srcdir)/gems/src/$$gem -Ilib ../../../.bundle/bin/rake; \
- done < $(srcdir)/gems/bundled_gems
-
-update-src::
- @$(CHDIR) "$(srcdir)" && LC_TIME=C exec $(VCSUP)
-
-update-download:: update-config_files
-
-after-update:: prereq
-
-gcov:
- $(Q) $(BASERUBY) $(srcdir)/tool/run-gcov.rb
-
-lcov:
- $(Q) $(BASERUBY) $(srcdir)/tool/run-lcov.rb
-
-update-benchmark-driver:
- $(Q) $(srcdir)/tool/git-refresh -C $(srcdir)/benchmark $(Q1:0=-q) \
- --branch $(BENCHMARK_DRIVER_GIT_REF) \
- $(BENCHMARK_DRIVER_GIT_URL) benchmark-driver $(GIT_OPTS)
-
-update-doclie:
- $(Q) $(srcdir)/tool/git-refresh -C $(srcdir)/coverage $(Q1:0=-q) \
- --branch $(DOCLIE_GIT_REF) \
- $(DOCLIE_GIT_URL) doclie $(GIT_OPTS)
-
-update-simplecov-html:
- $(Q) $(srcdir)/tool/git-refresh -C $(srcdir)/coverage $(Q1:0=-q) \
- --branch $(SIMPLECOV_HTML_GIT_REF) \
- $(SIMPLECOV_HTML_GIT_URL) simplecov-html $(GIT_OPTS)
-
-update-simplecov:
- $(Q) $(srcdir)/tool/git-refresh -C $(srcdir)/coverage $(Q1:0=-q) \
- --branch $(SIMPLECOV_GIT_REF) \
- $(SIMPLECOV_GIT_URL) simplecov $(GIT_OPTS)
-
-update-coverage: update-simplecov update-simplecov-html update-doclie
-
-INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \
- vmtc.inc vm.inc mjit_compile.inc
-
-$(INSNS): $(srcdir)/insns.def vm_opts.h \
- $(srcdir)/defs/opt_operand.def $(srcdir)/defs/opt_insn_unif.def \
- $(srcdir)/tool/insns2vm.rb
- $(ECHO) generating $@
- $(Q) $(BASERUBY) -Ku $(srcdir)/tool/insns2vm.rb $(INSNS2VMOPT) $@
-
-verconf.h: $(RBCONFIG)
-
-loadpath: verconf.h
- @$(CPP) $(XCFLAGS) $(CPPFLAGS) $(srcdir)/loadpath.c | \
- sed -e '1,/^const char ruby_initial_load_paths/d;/;/,$$d' \
- -e '/^ /!d;s/ *"\\0"$$//;s/" *"//g'
-
-un-runnable:
- $(ECHO) cannot make runnable, configure with --enable-load-relative.
- $(Q) exit 1
-
-mjit_config.h:
- $(ECHO) making $@
- @{ \
- quote() { \
- printf "#define $$1"; shift; \
- $${1+printf} $${1+' "%s"'$$sep} $${1+"$$@"}; \
- echo; \
- }; \
- parse_arch_flag() { \
- while [ "$$#" -gt 0 ]; do \
- case "$$1" in \
- -arch) \
- shift; \
- archs="$${archs:+$$archs }$$1"; \
- shift; \
- ;; \
- *) \
- arch_flag="$${arch_flag:+$${arch_flag} }$$1"; \
- shift; \
- ;; \
- esac; \
- done; \
- }; \
- test "$(Q)" = @ || set -x; \
- archs="$(UNIVERSAL_ARCHNAMES)"; \
- arch_flag=""; \
- parse_arch_flag $(ARCH_FLAG); \
- need_mjit_archflag="$${archs}$${arch_flag}"; \
- echo '#ifndef RUBY_MJIT_CONFIG_H'; \
- echo '#define RUBY_MJIT_CONFIG_H 1'; \
- echo; \
- sep=; \
- quote MJIT_BUILD_DIR "`$(CHDIR) . && pwd`"; \
- quote MJIT_MIN_HEADER_NAME "/$(MJIT_HEADER_INSTALL_DIR)/$(MJIT_MIN_HEADER_NAME)"; \
- sep=,; \
- quote "MJIT_CC_COMMON " $(MJIT_CC); \
- quote "MJIT_CFLAGS $${need_mjit_archflag:+ MJIT_ARCHFLAG}" -w; \
- quote "MJIT_OPTFLAGS " $(MJIT_OPTFLAGS); \
- quote "MJIT_DEBUGFLAGS " $(MJIT_DEBUGFLAGS); \
- quote "MJIT_LDSHARED " $(MJIT_LDSHARED); \
- quote "MJIT_DLDFLAGS $${need_mjit_archflag:+ MJIT_ARCHFLAG}" $(MJIT_DLDFLAGS); \
- quote "MJIT_LIBS " $(LIBRUBYARG_SHARED); \
- $${archs:+echo} $${archs:+'#if 0'}; \
- for arch in $$archs; do \
- echo "#elif defined __$${arch%=*}__"; \
- quote " MJIT_ARCHFLAG " -arch $${arch%=*}; \
- done; \
- $${archs:+echo} $${archs:+'#else'}; \
- if [ "$${arch_flag}" != "" ]; then \
- quote " MJIT_ARCHFLAG " $${arch_flag}; \
- fi; \
- $${archs:+echo} $${archs:+'#endif'}; \
- echo; \
- echo '#endif /* RUBY_MJIT_CONFIG_H */'; \
- } > $@
diff --git a/NEWS b/NEWS
deleted file mode 100644
index 8a54651d2d..0000000000
--- a/NEWS
+++ /dev/null
@@ -1,379 +0,0 @@
-# -*- markdown -*- markup: Markdown
-
-# NEWS for Ruby 2.6.0
-
-This document is a list of user visible feature changes made between
-releases except for bug fixes.
-
-Note that each entry is kept so brief that no reason behind or reference
-information is supplied with. For a full list of changes with all
-sufficient information, see the ChangeLog file or Redmine
-(e.g. `https://bugs.ruby-lang.org/issues/$FEATURE_OR_BUG_NUMBER`)
-
-## Changes since the 2.5.0 release
-
-### Language changes
-
-* `$SAFE` is a process global state and we can set 0 again.
- [Feature #14250]
-
-* refinements take place at block passing. [Feature #14223]
-
-* `else` without `rescue` now causes a syntax error. [EXPERIMENTAL]
-
-* constant names may start with a non-ASCII capital letter.
- [Feature #13770]
-
-* An endless range is introduced. You can write a range that has no end,
- like `(0..)`. The following shows typical use cases. [Feature #12912]
-
- ```ruby
- ary[1..] # identical to ary[1..-1]
- (1..).each {|index| ... } # infinite loop from index 1
- ary.zip(1..) {|elem, index| ... } # ary.each.with_index(1) { }
- ```
-
-* Non-`Symbol` key in keyword arguments hash causes an exception.
-
-### Core classes updates (outstanding ones only)
-
-* `Array`
-
- * Aliased methods:
-
- * `Array#filter` is a new alias for `Array#select`.
- [Feature #13784]
- * `Array#filter!` is a new alias for `Array#select!`.
- [Feature #13784]
-
-* `Binding`
-
- * New methods:
-
- * added `Binding#source_location`. [Feature #14230]
-
- This method returns the source location of binding, a 2-element
- array of `__FILE__` and `__LINE__`. Traditionally, the same
- information could be retrieved by `eval("[__FILE__, __LINE__]",
- binding)`, but we are planning to change this behavior so that
- `Kernel#eval` ignores binding's source location [Bug #4352].
- So, users should use this newly-introduced method instead of
- `Kernel#eval`.
-
-* `Dir`
-
- * New methods:
-
- * added `Dir#each_child` and `Dir#children` instance methods.
- [Feature #13969]
-
-* `Enumerable`
-
- * Aliased methods:
-
- * `Enumerable#filter` is a new alias for `Enumerable#select`.
- [Feature #13784]
-
-* `Enumerator::Lazy`
-
- * Aliased methods:
-
- * `Enumerator::Lazy#filter` is a new alias for
- `Enumerator::Lazy#select`. [Feature #13784]
-
-* `Exception`
-
- * New options:
-
- * `Exception#full_message` takes `:highlight` and `:order` options.
- [Bug #14324]
-
-* `Hash`
-
- * Aliased methods:
-
- * `Hash#filter` is a new alias for `Hash#select`. [Feature #13784]
-
- * `Hash#filter!` is a new alias for `Hash#select!`.
- [Feature #13784]
-
-* `IO`
-
- * New options:
-
- * new mode character `'x'` to open files for exclusive access.
- [Feature #11258]
-
-* `Kernel`
-
- * Aliased methods:
-
- * `Kernel#then` is a new alias for `Kernel#yield_self`.
- [Feature #14594]
-
- * New options:
-
- * `Kernel.#Complex`, `Kernel.#Float`, `Kernel.#Integer` and
- `Kernel.#Rational` take `:exception` option to specify the way of
- error handling. [Feature #12732]
-
- * `Kernel.#system` takes `:exception` option to raise an exception
- on failure. [Feature #14386]
-
- * Incompatible changes:
-
- * `system()` and `exec()` do not close non-standard file descriptors
- (The default of `:close_others` option is changed to `false` by
- default. but we still set the `FD_CLOEXEC` flag on descriptors we
- create). [Misc #14907]
-
-* `KeyError`
-
- * New options:
-
- * `KeyError#initialize` accepts `:receiver` and `:key` options to
- set receiver and key in Ruby code. [Feature #14313]
-
-* `Module`
-
- * New methods:
-
- * `Module#method_defined?`, `Module#private_method_defined?` and
- `Module#protected_method_defined?` now accepts the second
- parameter as optional. If it's `true` (=default), checks ancestor
- modules/classes, or checks only the class itself.
- [Feature #14944]
-
-* `NameError`
-
- * New options:
-
- * `NameError#initialize` accepts `:receiver` option to set receiver
- in Ruby code. [Feature #14313]
-
-* `NoMethodError`
-
- * New options:
-
- * `NoMethodError#initialize` accepts `:receiver` option to set
- receiver in Ruby code. [Feature #14313]
-
-* `Proc`
-
- * Incompatible changes:
-
- * `Proc#call` doesn't change `$SAFE` any more. [Feature #14250]
-
-* `Random`
-
- * New methods:
-
- * added `Random.bytes`. [Feature #4938]
-
-* `Range`
-
- * Incompatible changes:
-
- * `Range#===` now uses `#cover?` instead of `#include?` method.
- [Feature #14575]
- * `Range#cover?` now accepts Range object. [Feature #14473]
-
-* `RubyVM::AST`
-
- * New methods:
-
- * `RubyVM::AST.parse` parses a given string and returns AST nodes.
- [experimental]
-
- * `RubyVM::AST.parse_file` parses a given file and returns AST
- nodes. [experimental]
-
-* `String`
-
- * New features:
-
- * `String#split` yields each substring to the block if given.
- [Feature #4780]
-
-* `Struct`
-
- * Aliased method:
-
- * `Struct#filter` is a new alias for `Struct#select` [Feature #13784]
-
-* `TracePoint`
-
- * New methods:
-
- * `TracePoint#parameters` [Feature #14694]
-
-### Stdlib updates (outstanding ones only)
-
-* `ERB`
-
- * New options:
-
- * Add `:trim_mode` and `:eoutvar` keyword arguments to `ERB.new`.
- Now non-keyword arguments other than first one are softly
- deprecated and will be removed when Ruby 2.5 becomes EOL.
- [Feature #14256]
-
- * erb command's `-S` option is deprecated, which will be removed in
- the next version.
-
-* `FileUtils`
-
- * New method:
-
- * `FileUtils#cp_lr`. [Feature #4189]
-
-* `Matrix`
-
- * New method:
-
- * `Matrix#antisymmetric?`
-
-* `Net`
-
- * New options:
-
- * Add `:write_timeout` keyword argument to `Net::BufferedIO.new`.
- [Feature #13396]
-
- * New methods:
-
- * Add `Net::BufferedIO#write_timeout`,
- `Net::BufferedIO#write_timeout=`, `Net::HTTP#write_timeout`, and
- `Net::HTTP#write_timeout=`. [Feature #13396]
-
- * New constant:
-
- * Add `Net::HTTPClientException` to deprecate
- `Net::HTTPServerException`, whose name is misleading. [Bug #14688]
-
-* `REXML`
-
- * Improved some XPath implementations:
-
- * `concat()` function: Stringify all arguments before concatenating
-
- * `string()` function: Support context node
-
- * `string()` function: Support processing instruction node
-
- * Support `"*:#{ELEMENT_NAME}"` syntax in XPath 2.0
-
- * Fixed some XPath implementations:
-
- * `"//#{ELEMENT_NAME}[#{POSITION}]"` case
-
- * `string()` function: Fix `function(document)` returns nodes that
- are out of root elements.
-
- * `"/ #{ELEMENT_NAME} "` case
-
- * `"/ #{ELEMENT_NAME} [ #{PREDICATE} ]"` case
-
- * `"/ #{AXIS}:: #{ELEMENT_NAME} "` case
-
- * `"#{N}-#{M}"` case: One or more white spaces were required before
- `"-"`
-
- * `"/child::node()"` case
-
- * `"#{FUNCTION}()/#{PATH}"` case
-
- * `"@#{ATTRIBUTE}/parent::"` case
-
- * `"name(#{NODE_SET})"` case
-
-* `RSS`
-
- * New options:
-
- * `RSS::Parser.parse`: Accept options as `Hash`. `:validate`,
- `:ignore_unknown_element`, `:parser_class` options are available.
-
-* `Set`
-
- * Aliased methods:
-
- * `Set#filter!` is a new alias for `Set#select!`. [Feature #13784]
-
-* `URI`
-
- * New constant:
-
- * Add `URI::File` to handle file URI scheme. [Feature #14035]
-
-### Compatibility issues (excluding feature bug fixes)
-
-* `File`
-
- * `File.read`, `File.binread`, `File.write`, `File.binwrite`,
- `File.foreach`, and `File.readlines` do not invoke external commands
- even if the path starts with the pipe character `'|'`.
- [Feature #14245]
-
-* `Dir`
-
- * `Dir.glob` with `'\0'`-separated pattern list will be deprecated, and
- is now warned. [Feature #14643]
-
-### Stdlib compatibility issues (excluding feature bug fixes)
-
-### C API updates
-
-### Supported platform changes
-
-### Implementation improvements
-
-* Speedup `Proc#call` because we don't need to care about `$SAFE` any more.
- [Feature #14318]
-
- With `lc_fizzbuzz` benchmark which uses so many `Proc#call` we can
- measure x1.4 improvements. [Bug #10212]
-
-* Speedup `block.call` where `block` is passed block parameter.
- [Feature #14330]
-
- Ruby 2.5 improves block passing performance. [Feature #14045]
-
- Additionally, Ruby 2.6 improves the performance of passed block calling.
-
-* Introduce an initial implementation of JIT (Just-in-time) compiler.
- [Feature #14235] [experimental]
- * `--enable=jit` option is added to enable JIT. `--jit-verbose=1` is good for
- inspection. See `ruby --help` for others.
- * This JIT relies on C compiler used to build Ruby, on runtime. Only
- gcc and clang are supported for the JIT for now, and MinGW support has
- some issues.
- * As of 2.6.0-preview1, we're just preparing infrastructure for JIT and
- very few optimizations are implemented. So it's not ready for
- benchmarking Ruby's JIT performance yet. It's known that current JIT
- enablement makes Rails application slower for now.
- * rb_waitpid reimplemented on Unix-like platforms to maintain
- compatibility [Bug #14867]
-
-* VM generator script renewal; makes the generated VM more optimized.
- [GH-1779]
-
-* Thread cache enabled for pthreads platforms (for `Thread.new` and
- `Thread.start`). [Feature #14757]
-
-* timer thread is eliminated for platforms with POSIX timers [Misc #14937]
-
-### Miscellaneous changes
-
-* On macOS, shared libraries no longer include a full version number of ruby
- in their names. This eliminates the burden of each teeny upgrade on the
- platform that users need to rebuild every extension library.
-
- * Before:
- * libruby.2.6.0.dylib
- * libruby.2.6.dylib -> libruby.2.6.0.dylib
- * libruby.dylib -> libruby.2.6.0.dylib
-
- * After:
- * libruby.2.6.dylib
- * libruby.dylib -> libruby.2.6.dylib
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000000..f6c3c6fc97
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,820 @@
+# NEWS for Ruby 3.2.0
+
+This document is a list of user-visible feature changes
+since the **3.1.0** release, except for bug fixes.
+
+Note that each entry is kept to a minimum, see links for details.
+
+## Language changes
+
+* Anonymous rest and keyword rest arguments can now be passed as
+ arguments, instead of just used in method parameters.
+ [[Feature #18351]]
+
+ ```ruby
+ def foo(*)
+ bar(*)
+ end
+ def baz(**)
+ quux(**)
+ end
+ ```
+
+* A proc that accepts a single positional argument and keywords will
+ no longer autosplat. [[Bug #18633]]
+
+ ```ruby
+ proc{|a, **k| a}.call([1, 2])
+ # Ruby 3.1 and before
+ # => 1
+ # Ruby 3.2 and after
+ # => [1, 2]
+ ```
+
+* Constant assignment evaluation order for constants set on explicit
+ objects has been made consistent with single attribute assignment
+ evaluation order. With this code:
+
+ ```ruby
+ foo::BAR = baz
+ ```
+
+ `foo` is now called before `baz`. Similarly, for multiple assignments
+ to constants, left-to-right evaluation order is used. With this
+ code:
+
+ ```ruby
+ foo1::BAR1, foo2::BAR2 = baz1, baz2
+ ```
+
+ The following evaluation order is now used:
+
+ 1. `foo1`
+ 2. `foo2`
+ 3. `baz1`
+ 4. `baz2`
+
+ [[Bug #15928]]
+
+* "Find pattern" is no longer experimental.
+ [[Feature #18585]]
+
+* Methods taking a rest parameter (like `*args`) and wishing to delegate keyword
+ arguments through `foo(*args)` must now be marked with `ruby2_keywords`
+ (if not already the case). In other words, all methods wishing to delegate
+ keyword arguments through `*args` must now be marked with `ruby2_keywords`,
+ with no exception. This will make it easier to transition to other ways of
+ delegation once a library can require Ruby 3+. Previously, the `ruby2_keywords`
+ flag was kept if the receiving method took `*args`, but this was a bug and an
+ inconsistency. A good technique to find the potentially-missing `ruby2_keywords`
+ is to run the test suite, for where it fails find the last method which must
+ receive keyword arguments, use `puts nil, caller, nil` there, and check each
+ method/block on the call chain which must delegate keywords is correctly marked
+ as `ruby2_keywords`. [[Bug #18625]] [[Bug #16466]]
+
+ ```ruby
+ def target(**kw)
+ end
+
+ # Accidentally worked without ruby2_keywords in Ruby 2.7-3.1, ruby2_keywords
+ # needed in 3.2+. Just like (*args, **kwargs) or (...) would be needed on
+ # both #foo and #bar when migrating away from ruby2_keywords.
+ ruby2_keywords def bar(*args)
+ target(*args)
+ end
+
+ ruby2_keywords def foo(*args)
+ bar(*args)
+ end
+
+ foo(k: 1)
+ ```
+
+## Core classes updates
+
+Note: We're only listing outstanding class updates.
+
+* Fiber
+
+ * Introduce Fiber.[] and Fiber.[]= for inheritable fiber storage.
+ Introduce Fiber#storage and Fiber#storage= (experimental) for
+ getting and resetting the current storage. Introduce
+ `Fiber.new(storage:)` for setting the storage when creating a
+ fiber. [[Feature #19078]]
+
+ Existing Thread and Fiber local variables can be tricky to use.
+ Thread-local variables are shared between all fibers, making it
+ hard to isolate, while Fiber-local variables can be hard to
+ share. It is often desirable to define unit of execution
+ ("execution context") such that some state is shared between all
+ fibers and threads created in that context. This is what Fiber
+ storage provides.
+
+ ```ruby
+ def log(message)
+ puts "#{Fiber[:request_id]}: #{message}"
+ end
+
+ def handle_requests
+ while request = read_request
+ Fiber.schedule do
+ Fiber[:request_id] = SecureRandom.uuid
+
+ request.messages.each do |message|
+ Fiber.schedule do
+ log("Handling #{message}") # Log includes inherited request_id.
+ end
+ end
+ end
+ end
+ end
+ ```
+
+ You should generally consider Fiber storage for any state which
+ you want to be shared implicitly between all fibers and threads
+ created in a given context, e.g. a connection pool, a request
+ id, a logger level, environment variables, configuration, etc.
+
+* Fiber::Scheduler
+
+ * Introduce `Fiber::Scheduler#io_select` for non-blocking IO.select.
+ [[Feature #19060]]
+
+* IO
+
+ * Introduce IO#timeout= and IO#timeout which can cause
+ IO::TimeoutError to be raised if a blocking operation exceeds the
+ specified timeout. [[Feature #18630]]
+
+ ```ruby
+ STDIN.timeout = 1
+ STDIN.read # => Blocking operation timed out! (IO::TimeoutError)
+ ```
+
+ * Introduce `IO.new(..., path:)` and promote `File#path` to `IO#path`.
+ [[Feature #19036]]
+
+* Class
+
+ * Class#attached_object, which returns the object for which
+ the receiver is the singleton class. Raises TypeError if the
+ receiver is not a singleton class.
+ [[Feature #12084]]
+
+ ```ruby
+ class Foo; end
+
+ Foo.singleton_class.attached_object #=> Foo
+ Foo.new.singleton_class.attached_object #=> #<Foo:0x000000010491a370>
+ Foo.attached_object #=> TypeError: `Foo' is not a singleton class
+ nil.singleton_class.attached_object #=> TypeError: `NilClass' is not a singleton class
+ ```
+
+* Data
+
+ * New core class to represent simple immutable value object. The class is
+ similar to Struct and partially shares an implementation, but has more
+ lean and strict API. [[Feature #16122]]
+
+ ```ruby
+ Measure = Data.define(:amount, :unit)
+ distance = Measure.new(100, 'km') #=> #<data Measure amount=100, unit="km">
+ weight = Measure.new(amount: 50, unit: 'kg') #=> #<data Measure amount=50, unit="kg">
+ weight.with(amount: 40) #=> #<data Measure amount=40, unit="kg">
+ weight.amount #=> 50
+ weight.amount = 40 #=> NoMethodError: undefined method `amount='
+ ```
+
+* Encoding
+
+ * Encoding#replicate has been deprecated and will be removed in 3.3. [[Feature #18949]]
+ * The dummy `Encoding::UTF_16` and `Encoding::UTF_32` encodings no longer
+ try to dynamically guess the endian based on a byte order mark.
+ Use `Encoding::UTF_16BE`/`UTF_16LE` and `Encoding::UTF_32BE`/`UTF_32LE` instead.
+ This change speeds up getting the encoding of a String. [[Feature #18949]]
+ * Limit maximum encoding set size by 256.
+ If exceeding maximum size, `EncodingError` will be raised. [[Feature #18949]]
+
+* Enumerator
+
+ * Enumerator.product has been added. Enumerator::Product is the implementation. [[Feature #18685]]
+
+* Exception
+
+ * Exception#detailed_message has been added.
+ The default error printer calls this method on the Exception object
+ instead of #message. [[Feature #18564]]
+
+* Hash
+
+ * Hash#shift now always returns nil if the hash is
+ empty, instead of returning the default value or
+ calling the default proc. [[Bug #16908]]
+
+* Integer
+
+ * Integer#ceildiv has been added. [[Feature #18809]]
+
+* Kernel
+
+ * Kernel#binding raises RuntimeError if called from a non-Ruby frame
+ (such as a method defined in C). [[Bug #18487]]
+
+* MatchData
+
+ * MatchData#byteoffset has been added. [[Feature #13110]]
+ * MatchData#deconstruct has been added. [[Feature #18821]]
+ * MatchData#deconstruct_keys has been added. [[Feature #18821]]
+
+* Module
+
+ * Module.used_refinements has been added. [[Feature #14332]]
+ * Module#refinements has been added. [[Feature #12737]]
+ * Module#const_added has been added. [[Feature #17881]]
+ * Module#undefined_instance_methods has been added. [[Feature #12655]]
+
+* Proc
+
+ * Proc#dup returns an instance of subclass. [[Bug #17545]]
+ * Proc#parameters now accepts lambda keyword. [[Feature #15357]]
+
+* Process
+ * Added `RLIMIT_NPTS` constant to FreeBSD platform
+
+* Regexp
+
+ * The cache-based optimization is introduced.
+ Many (but not all) Regexp matching is now in linear time, which
+ will prevent regular expression denial of service (ReDoS)
+ vulnerability. [[Feature #19104]]
+
+ * Regexp.linear_time? is introduced. [[Feature #19194]]
+
+ * Regexp.new now supports passing the regexp flags not only as an Integer,
+ but also as a String. Unknown flags raise ArgumentError.
+ Otherwise, anything other than `true`, `false`, `nil` or Integer will be warned.
+ [[Feature #18788]]
+
+ * Regexp.timeout= has been added. Also, Regexp.new new supports timeout keyword.
+ See [[Feature #17837]]
+
+* Refinement
+
+ * Refinement#refined_class has been added. [[Feature #12737]]
+
+* RubyVM::AbstractSyntaxTree
+
+ * Add `error_tolerant` option for `parse`, `parse_file` and `of`. [[Feature #19013]]
+ With this option
+
+ 1. SyntaxError is suppressed
+ 2. AST is returned for invalid input
+ 3. `end` is complemented when a parser reaches to the end of input but `end` is insufficient
+ 4. `end` is treated as keyword based on indent
+
+ ```ruby
+ # Without error_tolerant option
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY)
+ def m
+ a = 10
+ if
+ end
+ RUBY
+ # => <internal:ast>:33:in `parse': syntax error, unexpected `end' (SyntaxError)
+
+ # With error_tolerant option
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
+ def m
+ a = 10
+ if
+ end
+ RUBY
+ p root # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-4:3>
+
+ # `end` is treated as keyword based on indent
+ root = RubyVM::AbstractSyntaxTree.parse(<<~RUBY, error_tolerant: true)
+ module Z
+ class Foo
+ foo.
+ end
+
+ def bar
+ end
+ end
+ RUBY
+ p root.children[-1].children[-1].children[-1].children[-2..-1]
+ # => [#<RubyVM::AbstractSyntaxTree::Node:CLASS@2:2-4:5>, #<RubyVM::AbstractSyntaxTree::Node:DEFN@6:2-7:5>]
+ ```
+
+ * Add `keep_tokens` option for `parse`, `parse_file` and `of`. Add `#tokens` and `#all_tokens`
+ for RubyVM::AbstractSyntaxTree::Node [[Feature #19070]]
+
+ ```ruby
+ root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ root.tokens.map{_1[2]}.join # => "x = 1 + 2"
+ ```
+
+* Set
+
+ * Set is now available as a built-in class without the need for `require "set"`. [[Feature #16989]]
+ It is currently autoloaded via the Set constant or a call to Enumerable#to_set.
+
+* String
+
+ * String#byteindex and String#byterindex have been added. [[Feature #13110]]
+ * Update Unicode to Version 15.0.0 and Emoji Version 15.0. [[Feature #18639]]
+ (also applies to Regexp)
+ * String#bytesplice has been added. [[Feature #18598]]
+ * String#dedup has been added as an alias to String#-@. [[Feature #18595]]
+
+* Struct
+
+ * A Struct class can also be initialized with keyword arguments
+ without `keyword_init: true` on Struct.new [[Feature #16806]]
+
+ ```ruby
+ Post = Struct.new(:id, :name)
+ Post.new(1, "hello") #=> #<struct Post id=1, name="hello">
+ # From Ruby 3.2, the following code also works without keyword_init: true.
+ Post.new(id: 1, name: "hello") #=> #<struct Post id=1, name="hello">
+ ```
+
+* Thread
+
+ * Thread.each_caller_location is added. [[Feature #16663]]
+
+* Thread::Queue
+
+ * Thread::Queue#pop(timeout: sec) is added. [[Feature #18774]]
+
+* Thread::SizedQueue
+
+ * Thread::SizedQueue#pop(timeout: sec) is added. [[Feature #18774]]
+ * Thread::SizedQueue#push(timeout: sec) is added. [[Feature #18944]]
+
+* Time
+
+ * Time#deconstruct_keys is added, allowing to use Time instances
+ in pattern-matching expressions [[Feature #19071]]
+
+ * Time.new now can parse a string like generated by Time#inspect
+ and return a Time instance based on the given argument.
+ [[Feature #18033]]
+
+* SyntaxError
+ * SyntaxError#path has been added. [[Feature #19138]]
+
+* TracePoint
+
+ * TracePoint#binding now returns `nil` for `c_call`/`c_return` TracePoints.
+ [[Bug #18487]]
+ * TracePoint#enable `target_thread` keyword argument now defaults to the
+ current thread if a block is given and `target` and `target_line` keyword
+ arguments are not passed. [[Bug #16889]]
+
+* UnboundMethod
+
+ * `UnboundMethod#==` returns `true` if the actual method is same. For example,
+ `String.instance_method(:object_id) == Array.instance_method(:object_id)`
+ returns `true`. [[Feature #18798]]
+
+ * `UnboundMethod#inspect` does not show the receiver of `instance_method`.
+ For example `String.instance_method(:object_id).inspect` returns
+ `"#<UnboundMethod: Kernel#object_id()>"`
+ (was `"#<UnboundMethod: String(Kernel)#object_id()>"`).
+
+* GC
+
+ * Expose `need_major_gc` via `GC.latest_gc_info`. [GH-6791]
+
+* ObjectSpace
+
+ * `ObjectSpace.dump_all` dump shapes as well. [GH-6868]
+
+## Stdlib updates
+
+* Bundler
+
+ * Bundler now uses [PubGrub] resolver instead of [Molinillo] for performance improvement.
+ * Add --ext=rust support to bundle gem for creating simple gems with Rust extensions.
+ [[GH-rubygems-6149]]
+ * Make cloning git repos faster [[GH-rubygems-4475]]
+
+* RubyGems
+
+ * Add mswin support for cargo builder. [[GH-rubygems-6167]]
+
+* CGI
+
+ * `CGI.escapeURIComponent` and `CGI.unescapeURIComponent` are added.
+ [[Feature #18822]]
+
+* Coverage
+
+ * `Coverage.setup` now accepts `eval: true`. By this, `eval` and related methods are
+ able to generate code coverage. [[Feature #19008]]
+
+ * `Coverage.supported?(mode)` enables detection of what coverage modes are
+ supported. [[Feature #19026]]
+
+* Date
+
+ * Added `Date#deconstruct_keys` and `DateTime#deconstruct_keys` same as [[Feature #19071]]
+
+* ERB
+
+ * `ERB::Util.html_escape` is made faster than `CGI.escapeHTML`.
+ * It no longer allocates a String object when no character needs to be escaped.
+ * It skips calling `#to_s` method when an argument is already a String.
+ * `ERB::Escape.html_escape` is added as an alias to `ERB::Util.html_escape`,
+ which has not been monkey-patched by Rails.
+ * `ERB::Util.url_encode` is made faster using `CGI.escapeURIComponent`.
+ * `-S` option is removed from `erb` command.
+
+* FileUtils
+
+ * Add FileUtils.ln_sr method and `relative:` option to FileUtils.ln_s.
+ [[Feature #18925]]
+
+* IRB
+
+ * debug.gem integration commands have been added: `debug`, `break`, `catch`,
+ `next`, `delete`, `step`, `continue`, `finish`, `backtrace`, `info`
+ * They work even if you don't have `gem "debug"` in your Gemfile.
+ * See also: [What's new in Ruby 3.2's IRB?](https://st0012.dev/whats-new-in-ruby-3-2-irb)
+ * More Pry-like commands and features have been added.
+ * `edit` and `show_cmds` (like Pry's `help`) are added.
+ * `ls` takes `-g` or `-G` option to filter out outputs.
+ * `show_source` is aliased from `$` and accepts unquoted inputs.
+ * `whereami` is aliased from `@`.
+
+* Net::Protocol
+
+ * Improve `Net::BufferedIO` performance. [[GH-net-protocol-14]]
+
+* Pathname
+
+ * Added `Pathname#lutime`. [[GH-pathname-20]]
+
+* Socket
+
+ * Added the following constants for supported platforms.
+ * `SO_INCOMING_CPU`
+ * `SO_INCOMING_NAPI_ID`
+ * `SO_RTABLE`
+ * `SO_SETFIB`
+ * `SO_USER_COOKIE`
+ * `TCP_KEEPALIVE`
+ * `TCP_CONNECTION_INFO`
+
+* SyntaxSuggest
+
+ * The feature of `syntax_suggest` formerly `dead_end` is integrated in Ruby.
+ [[Feature #18159]]
+
+* UNIXSocket
+
+ * Add support for UNIXSocket on Windows. Emulate anonymous sockets. Add
+ support for File.socket? and File::Stat#socket? where possible.
+ [[Feature #19135]]
+
+* The following default gems are updated.
+
+ * RubyGems 3.4.1
+ * abbrev 0.1.1
+ * benchmark 0.2.1
+ * bigdecimal 3.1.3
+ * bundler 2.4.1
+ * cgi 0.3.6
+ * csv 3.2.6
+ * date 3.3.3
+ * delegate 0.3.0
+ * did_you_mean 1.6.3
+ * digest 3.1.1
+ * drb 2.1.1
+ * english 0.7.2
+ * erb 4.0.2
+ * error_highlight 0.5.1
+ * etc 1.4.2
+ * fcntl 1.0.2
+ * fiddle 1.1.1
+ * fileutils 1.7.0
+ * forwardable 1.3.3
+ * getoptlong 0.2.0
+ * io-console 0.6.0
+ * io-nonblock 0.2.0
+ * io-wait 0.3.0
+ * ipaddr 1.2.5
+ * irb 1.6.2
+ * json 2.6.3
+ * logger 1.5.3
+ * mutex_m 0.1.2
+ * net-http 0.4.0
+ * net-protocol 0.2.1
+ * nkf 0.1.2
+ * open-uri 0.3.0
+ * open3 0.1.2
+ * openssl 3.1.0
+ * optparse 0.3.1
+ * ostruct 0.5.5
+ * pathname 0.2.1
+ * pp 0.4.0
+ * pstore 0.1.2
+ * psych 5.0.1
+ * racc 1.6.2
+ * rdoc 6.5.0
+ * readline-ext 0.1.5
+ * reline 0.3.2
+ * resolv 0.2.2
+ * resolv-replace 0.1.1
+ * securerandom 0.2.2
+ * set 1.0.3
+ * stringio 3.0.4
+ * strscan 3.0.5
+ * syntax_suggest 1.0.2
+ * syslog 0.1.1
+ * tempfile 0.1.3
+ * time 0.2.1
+ * timeout 0.3.1
+ * tmpdir 0.1.3
+ * tsort 0.1.1
+ * un 0.2.1
+ * uri 0.12.0
+ * weakref 0.1.2
+ * win32ole 1.8.9
+ * yaml 0.2.1
+ * zlib 3.0.0
+
+* The following bundled gems are updated.
+
+ * minitest 5.16.3
+ * power_assert 2.0.3
+ * test-unit 3.5.7
+ * net-ftp 0.2.0
+ * net-imap 0.3.4
+ * net-pop 0.1.2
+ * net-smtp 0.3.3
+ * rbs 2.8.2
+ * typeprof 0.21.3
+ * debug 1.7.1
+
+See GitHub releases like [GitHub Releases of Logger](https://github.com/ruby/logger/releases) or changelog for details of the default gems or bundled gems.
+
+## Supported platforms
+
+* WebAssembly/WASI is added. See [wasm/README.md] and [ruby.wasm] for more details. [[Feature #18462]]
+
+## Compatibility issues
+
+* `String#to_c` currently treat a sequence of underscores as an end of Complex
+ string. [[Bug #19087]]
+
+* Now `ENV.clone` raises `TypeError` as well as `ENV.dup` [[Bug #17767]]
+
+### Removed constants
+
+The following deprecated constants are removed.
+
+* `Fixnum` and `Bignum` [[Feature #12005]]
+* `Random::DEFAULT` [[Feature #17351]]
+* `Struct::Group`
+* `Struct::Passwd`
+
+### Removed methods
+
+The following deprecated methods are removed.
+
+* `Dir.exists?` [[Feature #17391]]
+* `File.exists?` [[Feature #17391]]
+* `Kernel#=~` [[Feature #15231]]
+* `Kernel#taint`, `Kernel#untaint`, `Kernel#tainted?`
+ [[Feature #16131]]
+* `Kernel#trust`, `Kernel#untrust`, `Kernel#untrusted?`
+ [[Feature #16131]]
+* `Method#public?`, `Method#private?`, `Method#protected?`,
+ `UnboundMethod#public?`, `UnboundMethod#private?`, `UnboundMethod#protected?`
+ [[Bug #18729]] [[Bug #18751]] [[Bug #18435]]
+
+### Source code incompatibility of extension libraries
+
+* Extension libraries provide PRNG, subclasses of Random, need updates.
+ See [PRNG update] below for more information. [[Bug #19100]]
+
+### Error printer
+
+* Ruby no longer escapes control characters and backslashes in an
+ error message. [[Feature #18367]]
+
+### Constant lookup when defining a class/module
+
+* When defining a class/module directly under the Object class by class/module
+ statement, if there is already a class/module defined by `Module#include`
+ with the same name, the statement was handled as "open class" in Ruby 3.1 or before.
+ Since Ruby 3.2, a new class is defined instead. [[Feature #18832]]
+
+## Stdlib compatibility issues
+
+* Psych no longer bundles libyaml sources.
+ And also Fiddle no longer bundles libffi sources.
+ Users need to install the libyaml/libffi library themselves via the package
+ manager like apt, yum, brew, etc.
+
+ Psych and fiddle supported the static build with specific version of libyaml
+ and libffi sources. You can build psych with libyaml-0.2.5 like this.
+
+ ```bash
+ $ ./configure --with-libyaml-source-dir=/path/to/libyaml-0.2.5
+ ```
+
+ And you can build fiddle with libffi-3.4.4 like this.
+
+ ```bash
+ $ ./configure --with-libffi-source-dir=/path/to/libffi-3.4.4
+ ```
+
+ [[Feature #18571]]
+
+* Check cookie name/path/domain characters in `CGI::Cookie`. [[CVE-2021-33621]]
+
+* `URI.parse` return empty string in host instead of nil. [[sec-156615]]
+
+## C API updates
+
+### Updated C APIs
+
+The following APIs are updated.
+
+* PRNG update
+
+ `rb_random_interface_t` in ruby/random.h updated and versioned.
+ Extension libraries which use this interface and built for older
+ versions need to rebuild with adding `init_int32` function.
+
+### Added C APIs
+
+* `VALUE rb_hash_new_capa(long capa)` was added to created hashes with the desired capacity.
+* `rb_internal_thread_add_event_hook` and `rb_internal_thread_add_event_hook` were added to instrument threads scheduling.
+ The following events are available:
+ * `RUBY_INTERNAL_THREAD_EVENT_STARTED`
+ * `RUBY_INTERNAL_THREAD_EVENT_READY`
+ * `RUBY_INTERNAL_THREAD_EVENT_RESUMED`
+ * `RUBY_INTERNAL_THREAD_EVENT_SUSPENDED`
+ * `RUBY_INTERNAL_THREAD_EVENT_EXITED`
+* `rb_debug_inspector_current_depth` and `rb_debug_inspector_frame_depth` are added for debuggers.
+
+### Removed C APIs
+
+The following deprecated APIs are removed.
+
+* `rb_cData` variable.
+* "taintedness" and "trustedness" functions. [[Feature #16131]]
+
+## Implementation improvements
+
+* Fixed several race conditions in Kernel#autoload. [[Bug #18782]]
+* Cache invalidation for expressions referencing constants is now
+ more fine-grained. `RubyVM.stat(:global_constant_state)` was
+ removed because it was closely tied to the previous caching scheme
+ where setting any constant invalidates all caches in the system.
+ New keys, `:constant_cache_invalidations` and `:constant_cache_misses`,
+ were introduced to help with use cases for `:global_constant_state`.
+ [[Feature #18589]]
+* The cache-based optimization for Regexp matching is introduced.
+ [[Feature #19104]]
+* [Variable Width Allocation](https://shopify.engineering/ruby-variable-width-allocation)
+ is now enabled by default. [[Feature #18239]]
+* Added a new instance variable caching mechanism, called object shapes, which
+ improves inline cache hits for most objects and allows us to generate very
+ efficient JIT code. Objects whose instance variables are defined in a
+ consistent order will see the most performance benefits.
+ [[Feature #18776]]
+* Speed up marking instruction sequences by using a bitmap to find "markable"
+ objects. This change results in faster major collections.
+ [[Feature #18875]]
+
+## JIT
+
+### YJIT
+
+* YJIT is no longer experimental
+ * Has been tested on production workloads for over a year and proven to be quite stable.
+* YJIT now supports both x86-64 and arm64/aarch64 CPUs on Linux, MacOS, BSD and other UNIX platforms.
+ * This release brings support for Mac M1/M2, AWS Graviton and Raspberry Pi 4.
+* Building YJIT now requires Rust 1.58.0+. [[Feature #18481]]
+ * In order to ensure that CRuby is built with YJIT, please install `rustc` >= 1.58.0
+ before running `./configure`
+ * Please reach out to the YJIT team should you run into any issues.
+* Physical memory for JIT code is lazily allocated. Unlike Ruby 3.1,
+ the RSS of a Ruby process is minimized because virtual memory pages
+ allocated by `--yjit-exec-mem-size` will not be mapped to physical
+ memory pages until actually utilized by JIT code.
+* Introduce Code GC that frees all code pages when the memory consumption
+ by JIT code reaches `--yjit-exec-mem-size`.
+ * `RubyVM::YJIT.runtime_stats` returns Code GC metrics in addition to
+ existing `inline_code_size` and `outlined_code_size` keys:
+ `code_gc_count`, `live_page_count`, `freed_page_count`, and `freed_code_size`.
+* Most of the statistics produced by `RubyVM::YJIT.runtime_stats` are now available in release builds.
+ * Simply run ruby with `--yjit-stats` to compute and dump stats (incurs some run-time overhead).
+* YJIT is now optimized to take advantage of object shapes. [[Feature #18776]]
+* Take advantage of finer-grained constant invalidation to invalidate less code when defining new constants. [[Feature #18589]]
+* The default `--yjit-exec-mem-size` is changed to 64 (MiB).
+* The default `--yjit-call-threshold` is changed to 30.
+
+### MJIT
+
+* The MJIT compiler is re-implemented in Ruby as `ruby_vm/mjit/compiler`.
+* MJIT compiler is executed under a forked Ruby process instead of
+ doing it in a native thread called MJIT worker. [[Feature #18968]]
+ * As a result, Microsoft Visual Studio (MSWIN) is no longer supported.
+* MinGW is no longer supported. [[Feature #18824]]
+* Rename `--mjit-min-calls` to `--mjit-call-threshold`.
+* Change default `--mjit-max-cache` back from 10000 to 100.
+
+[Feature #12005]: https://bugs.ruby-lang.org/issues/12005
+[Feature #12084]: https://bugs.ruby-lang.org/issues/12084
+[Feature #12655]: https://bugs.ruby-lang.org/issues/12655
+[Feature #12737]: https://bugs.ruby-lang.org/issues/12737
+[Feature #13110]: https://bugs.ruby-lang.org/issues/13110
+[Feature #14332]: https://bugs.ruby-lang.org/issues/14332
+[Feature #15231]: https://bugs.ruby-lang.org/issues/15231
+[Feature #15357]: https://bugs.ruby-lang.org/issues/15357
+[Bug #15928]: https://bugs.ruby-lang.org/issues/15928
+[Feature #16122]: https://bugs.ruby-lang.org/issues/16122
+[Feature #16131]: https://bugs.ruby-lang.org/issues/16131
+[Bug #16466]: https://bugs.ruby-lang.org/issues/16466
+[Feature #16663]: https://bugs.ruby-lang.org/issues/16663
+[Feature #16806]: https://bugs.ruby-lang.org/issues/16806
+[Bug #16889]: https://bugs.ruby-lang.org/issues/16889
+[Bug #16908]: https://bugs.ruby-lang.org/issues/16908
+[Feature #16989]: https://bugs.ruby-lang.org/issues/16989
+[Feature #17351]: https://bugs.ruby-lang.org/issues/17351
+[Feature #17391]: https://bugs.ruby-lang.org/issues/17391
+[Bug #17545]: https://bugs.ruby-lang.org/issues/17545
+[Bug #17767]: https://bugs.ruby-lang.org/issues/17767
+[Feature #17837]: https://bugs.ruby-lang.org/issues/17837
+[Feature #17881]: https://bugs.ruby-lang.org/issues/17881
+[Feature #18033]: https://bugs.ruby-lang.org/issues/18033
+[Feature #18159]: https://bugs.ruby-lang.org/issues/18159
+[Feature #18239]: https://bugs.ruby-lang.org/issues/18239#note-17
+[Feature #18351]: https://bugs.ruby-lang.org/issues/18351
+[Feature #18367]: https://bugs.ruby-lang.org/issues/18367
+[Bug #18435]: https://bugs.ruby-lang.org/issues/18435
+[Feature #18462]: https://bugs.ruby-lang.org/issues/18462
+[Feature #18481]: https://bugs.ruby-lang.org/issues/18481
+[Bug #18487]: https://bugs.ruby-lang.org/issues/18487
+[Feature #18564]: https://bugs.ruby-lang.org/issues/18564
+[Feature #18571]: https://bugs.ruby-lang.org/issues/18571
+[Feature #18585]: https://bugs.ruby-lang.org/issues/18585
+[Feature #18589]: https://bugs.ruby-lang.org/issues/18589
+[Feature #18595]: https://bugs.ruby-lang.org/issues/18595
+[Feature #18598]: https://bugs.ruby-lang.org/issues/18598
+[Bug #18625]: https://bugs.ruby-lang.org/issues/18625
+[Feature #18630]: https://bugs.ruby-lang.org/issues/18630
+[Bug #18633]: https://bugs.ruby-lang.org/issues/18633
+[Feature #18639]: https://bugs.ruby-lang.org/issues/18639
+[Feature #18685]: https://bugs.ruby-lang.org/issues/18685
+[Bug #18729]: https://bugs.ruby-lang.org/issues/18729
+[Bug #18751]: https://bugs.ruby-lang.org/issues/18751
+[Feature #18774]: https://bugs.ruby-lang.org/issues/18774
+[Feature #18776]: https://bugs.ruby-lang.org/issues/18776
+[Bug #18782]: https://bugs.ruby-lang.org/issues/18782
+[Feature #18788]: https://bugs.ruby-lang.org/issues/18788
+[Feature #18798]: https://bugs.ruby-lang.org/issues/18798
+[Feature #18809]: https://bugs.ruby-lang.org/issues/18809
+[Feature #18821]: https://bugs.ruby-lang.org/issues/18821
+[Feature #18822]: https://bugs.ruby-lang.org/issues/18822
+[Feature #18824]: https://bugs.ruby-lang.org/issues/18824
+[Feature #18832]: https://bugs.ruby-lang.org/issues/18832
+[Feature #18875]: https://bugs.ruby-lang.org/issues/18875
+[Feature #18925]: https://bugs.ruby-lang.org/issues/18925
+[Feature #18944]: https://bugs.ruby-lang.org/issues/18944
+[Feature #18949]: https://bugs.ruby-lang.org/issues/18949
+[Feature #18968]: https://bugs.ruby-lang.org/issues/18968
+[Feature #19008]: https://bugs.ruby-lang.org/issues/19008
+[Feature #19013]: https://bugs.ruby-lang.org/issues/19013
+[Feature #19026]: https://bugs.ruby-lang.org/issues/19026
+[Feature #19036]: https://bugs.ruby-lang.org/issues/19036
+[Feature #19060]: https://bugs.ruby-lang.org/issues/19060
+[Feature #19070]: https://bugs.ruby-lang.org/issues/19070
+[Feature #19071]: https://bugs.ruby-lang.org/issues/19071
+[Feature #19078]: https://bugs.ruby-lang.org/issues/19078
+[Bug #19087]: https://bugs.ruby-lang.org/issues/19087
+[Bug #19100]: https://bugs.ruby-lang.org/issues/19100
+[Feature #19104]: https://bugs.ruby-lang.org/issues/19104
+[Feature #19135]: https://bugs.ruby-lang.org/issues/19135
+[Feature #19138]: https://bugs.ruby-lang.org/issues/19138
+[Feature #19194]: https://bugs.ruby-lang.org/issues/19194
+[Molinillo]: https://github.com/CocoaPods/Molinillo
+[PubGrub]: https://github.com/jhawthorn/pub_grub
+[GH-net-protocol-14]: https://github.com/ruby/net-protocol/pull/14
+[GH-pathname-20]: https://github.com/ruby/pathname/pull/20
+[GH-6791]: https://github.com/ruby/ruby/pull/6791
+[GH-6868]: https://github.com/ruby/ruby/pull/6868
+[GH-rubygems-4475]: https://github.com/rubygems/rubygems/pull/4475
+[GH-rubygems-6149]: https://github.com/rubygems/rubygems/pull/6149
+[GH-rubygems-6167]: https://github.com/rubygems/rubygems/pull/6167
+[sec-156615]: https://hackerone.com/reports/156615
+[CVE-2021-33621]: https://www.ruby-lang.org/en/news/2022/11/22/http-response-splitting-in-cgi-cve-2021-33621/
+[wasm/README.md]: https://github.com/ruby/ruby/blob/master/wasm/README.md
+[ruby.wasm]: https://github.com/ruby/ruby.wasm
diff --git a/README.ja.md b/README.ja.md
index 353f9b14cc..93c0131690 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -1,5 +1,9 @@
-[![Build Status](https://travis-ci.org/ruby/ruby.svg)](https://travis-ci.org/ruby/ruby)
-[![Build status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/trunk?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/trunk)
+[![Actions Status: MinGW](https://github.com/ruby/ruby/workflows/MinGW/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW")
+[![Actions Status: MJIT](https://github.com/ruby/ruby/workflows/MJIT/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"MJIT")
+[![Actions Status: Ubuntu](https://github.com/ruby/ruby/workflows/Ubuntu/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu")
+[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
+[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)
+[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)
# Rubyã¨ã¯
@@ -13,7 +17,7 @@ Rubyã¯ãƒ†ã‚­ã‚¹ãƒˆå‡¦ç†é–¢ä¿‚ã®èƒ½åŠ›ãªã©ã«å„ªã‚Œï¼ŒPerlã¨åŒã˜ãらã„
* ã‚·ãƒ³ãƒ—ãƒ«ãªæ–‡æ³•
* 普通ã®ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆæŒ‡å‘æ©Ÿèƒ½(クラス,メソッドコールãªã©)
-* 特殊ãªã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆæŒ‡å‘æ©Ÿèƒ½(Mixin, 特異メソッドãªã©)
+* 特殊ãªã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆæŒ‡å‘æ©Ÿèƒ½(Mixin,特異メソッドãªã©)
* 演算å­ã‚ªãƒ¼ãƒãƒ¼ãƒ­ãƒ¼ãƒ‰
* ä¾‹å¤–å‡¦ç†æ©Ÿèƒ½
* イテレータã¨ã‚¯ãƒ­ãƒ¼ã‚¸ãƒ£
@@ -21,7 +25,7 @@ Rubyã¯ãƒ†ã‚­ã‚¹ãƒˆå‡¦ç†é–¢ä¿‚ã®èƒ½åŠ›ãªã©ã«å„ªã‚Œï¼ŒPerlã¨åŒã˜ãらã„
* ダイナミックローディング (アーキテクãƒãƒ£ã«ã‚ˆã‚‹)
* ç§»æ¤æ€§ãŒé«˜ã„.多ãã®Unix-like/POSIX互æ›ãƒ—ラットフォーム上ã§å‹•ãã ã‘ã§ãªã,Windows, macOS,
Haikuãªã©ã®ä¸Šã§ã‚‚å‹•ã cf.
- https://github.com/ruby/ruby/blob/trunk/doc/contributing.rdoc#platform-maintainers
+ https://github.com/ruby/ruby/blob/master/doc/contributing.rdoc#platform-maintainers
## 入手法
@@ -32,21 +36,29 @@ https://www.ruby-lang.org/ja/downloads/
ã‚’å‚ç…§ã—ã¦ãã ã•ã„.
-### Subversionã§
+### Git
-開発先端ã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§å–å¾—ã§ãã¾ã™ï¼Ž
+ミラーをGitHubã«å…¬é–‹ã—ã¦ã„ã¾ã™ï¼Ž 以下ã®ã‚³ãƒžãƒ³ãƒ‰ã§ãƒªãƒã‚¸ãƒˆãƒªã‚’å–å¾—ã§ãã¾ã™ï¼Ž
- $ svn co https://svn.ruby-lang.org/repos/ruby/trunk/ ruby
+ $ git clone https://github.com/ruby/ruby.git
-ä»–ã«é–‹ç™ºä¸­ã®ãƒ–ランãƒã®ä¸€è¦§ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§è¦‹ã‚‰ã‚Œã¾ã™ï¼Ž
+ä»–ã®ãƒ–ランãƒã®ä¸€è¦§ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§è¦‹ã‚‰ã‚Œã¾ã™ï¼Ž
- $ svn ls https://svn.ruby-lang.org/repos/ruby/branches/
+ $ git ls-remote https://github.com/ruby/ruby.git
-### Gitã§
+Rubyリãƒã‚¸ãƒˆãƒªã®æœ¬æ¥ã®master㯠https://git.ruby-lang.org/ruby.git ã«ã‚りã¾ã™ï¼Ž
+コミッタã¯ã“ã¡ã‚‰ã‚’使ã„ã¾ã™ï¼Ž
-Subversionã®ãƒŸãƒ©ãƒ¼ã‚’GitHubã«å…¬é–‹ã—ã¦ã„ã¾ã™ï¼Ž 以下ã®ã‚³ãƒžãƒ³ãƒ‰ã§ãƒªãƒã‚¸ãƒˆãƒªã‚’å–å¾—ã§ãã¾ã™ï¼Ž
+### Subversion
+
+å¤ã„Rubyã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§ã‚‚å–å¾—ã§ãã¾ã™ï¼Ž
+
+ $ svn co https://svn.ruby-lang.org/repos/ruby/branches/ruby_2_6/ ruby
+
+ä»–ã®ãƒ–ランãƒã®ä¸€è¦§ã¯æ¬¡ã®ã‚³ãƒžãƒ³ãƒ‰ã§è¦‹ã‚‰ã‚Œã¾ã™ï¼Ž
+
+ $ svn ls https://svn.ruby-lang.org/repos/ruby/branches/
- $ git clone https://github.com/ruby/ruby.git
## ホームページ
@@ -58,38 +70,33 @@ https://www.ruby-lang.org/
## メーリングリスト
-Rubyã®ãƒ¡ãƒ¼ãƒªãƒ³ã‚°ãƒªã‚¹ãƒˆãŒã‚りã¾ã™ï¼Žå‚åŠ å¸Œæœ›ã®æ–¹ã¯
-
-mailto:ruby-list-request@ruby-lang.org
-
-ã¾ã§æœ¬æ–‡ã«
+Rubyã®ãƒ¡ãƒ¼ãƒªãƒ³ã‚°ãƒªã‚¹ãƒˆãŒã‚りã¾ã™ï¼Žå‚åŠ å¸Œæœ›ã®æ–¹ã¯ [ruby-list-request@ruby-lang.org] ã¾ã§æœ¬æ–‡ã«
subscribe
ã¨æ›¸ã„ã¦é€ã£ã¦ä¸‹ã•ã„.
-Ruby開発者å‘ã‘メーリングリストもã‚りã¾ã™ï¼Žã“ã¡ã‚‰ã§ã¯rubyã®ãƒã‚°ï¼Œå°†æ¥ã®ä»•様拡張ãªã©å®Ÿè£…上ã®å•題ã«ã¤ã„ã¦è­°è«–ã•れã¦ã„ã¾ã™ï¼Ž å‚åŠ å¸Œæœ›ã®æ–¹ã¯
-
-mailto:ruby-dev-request@ruby-lang.org
-
-ã¾ã§ruby-listã¨åŒæ§˜ã®æ–¹æ³•ã§ãƒ¡ãƒ¼ãƒ«ã—ã¦ãã ã•ã„.
+Ruby開発者å‘ã‘メーリングリストもã‚りã¾ã™ï¼Žã“ã¡ã‚‰ã§ã¯rubyã®ãƒã‚°ï¼Œå°†æ¥ã®ä»•様拡張ãªã©å®Ÿè£…上ã®å•題ã«ã¤ã„ã¦è­°è«–ã•れã¦ã„ã¾ã™ï¼Ž
+å‚åŠ å¸Œæœ›ã®æ–¹ã¯ [ruby-dev-request@ruby-lang.org] ã¾ã§ruby-listã¨åŒæ§˜ã®æ–¹æ³•ã§ãƒ¡ãƒ¼ãƒ«ã—ã¦ãã ã•ã„.
Ruby拡張モジュールã«ã¤ã„ã¦è©±ã—åˆã†ruby-extãƒ¡ãƒ¼ãƒªãƒ³ã‚°ãƒªã‚¹ãƒˆã¨æ•°å­¦é–¢ä¿‚ã®è©±é¡Œã«ã¤ã„ã¦è©±ã—åˆã†ruby-mathメーリングリストã¨
英語ã§rubyã«ã¤ã„ã¦è©±ã—åˆã†ruby-talkメーリングリストもã‚りã¾ã™ï¼Žå‚加方法ã¯ã©ã‚Œã‚‚åŒã˜ã§ã™ï¼Ž
+[ruby-list-request@ruby-lang.org]: mailto:ruby-list-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe
+[ruby-dev-request@ruby-lang.org]: mailto:ruby-dev-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe
+
## コンパイル・インストール
ä»¥ä¸‹ã®æ‰‹é †ã§è¡Œã£ã¦ãã ã•ã„.
-1. ã‚‚ã— `configure` ファイルãŒè¦‹ã¤ã‹ã‚‰ãªã„,もã—ã㯠`configure.ac` よりå¤ã„よã†ãªã‚‰ï¼Œ `autoconf` を実行ã—ã¦
- æ–°ã—ã `configure` を生æˆã™ã‚‹
+1. (Gitリãƒã‚¸ãƒˆãƒªã‹ã‚‰å–å¾—ã—ãŸã‚½ãƒ¼ã‚¹ã‚’ビルドã™ã‚‹å ´åˆ) `./autogen.sh` を実行ã—ã¦æ–°ã—ã `configure` を生æˆã™ã‚‹
2. `configure` を実行ã—㦠`Makefile` ãªã©ã‚’生æˆã™ã‚‹
環境ã«ã‚ˆã£ã¦ã¯ãƒ‡ãƒ•ォルトã®Cコンパイラ用オプションãŒä»˜ãã¾ã™ï¼Ž `configure` オプション㧠`optflags=..`
`warnflags=..` ç­‰ã§ä¸Šæ›¸ãã§ãã¾ã™ï¼Ž
-3. (å¿…è¦ãªã‚‰ã°)`defines.h` を編集ã™ã‚‹
+3. (å¿…è¦ãªã‚‰ã°)`include/ruby/defines.h` を編集ã™ã‚‹
多分,必è¦ç„¡ã„ã¨æ€ã„ã¾ã™ï¼Ž
@@ -98,7 +105,8 @@ Ruby拡張モジュールã«ã¤ã„ã¦è©±ã—åˆã†ruby-extメーリングリスãƒ
`ext/Setup` ã«è¨˜è¿°ã—ãŸãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã¯é™çš„ã«ãƒªãƒ³ã‚¯ã•れã¾ã™ï¼Ž
ダイナミックローディングをサãƒãƒ¼ãƒˆã—ã¦ã„ãªã„アーキテクãƒãƒ£ã§ã¯ `Setup` ã®1行目ã®ã€Œ`option nodynamic`ã€ã¨ã„ã†è¡Œã®ã‚³
- メントを外ã™å¿…è¦ãŒã‚りã¾ã™ï¼Žã¾ãŸï¼Œã“ã®ã‚¢ãƒ¼ã‚­ãƒ†ã‚¯ãƒãƒ£ã§æ‹¡å¼µãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’利用ã™ã‚‹ãŸã‚ã«ã¯ï¼Œã‚らã‹ã˜ã‚é™çš„ã«ãƒªãƒ³ã‚¯ã‚’ã—ã¦ãŠãå¿…è¦ãŒã‚りã¾ã™ï¼Ž
+ メントを外ã™å¿…è¦ãŒã‚りã¾ã™ï¼Ž
+ ã¾ãŸï¼Œã“ã®ã‚¢ãƒ¼ã‚­ãƒ†ã‚¯ãƒãƒ£ã§æ‹¡å¼µãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’利用ã™ã‚‹ãŸã‚ã«ã¯ï¼Œã‚らã‹ã˜ã‚é™çš„ã«ãƒªãƒ³ã‚¯ã‚’ã—ã¦ãŠãå¿…è¦ãŒã‚りã¾ã™ï¼Ž
5. `make` を実行ã—ã¦ã‚³ãƒ³ãƒ‘イルã™ã‚‹
@@ -158,17 +166,17 @@ UNIXã§ã‚れ㰠`configure` ãŒã»ã¨ã‚“ã©ã®å·®ç•°ã‚’å¸åŽã—ã¦ãれるã¯
## フィードãƒãƒƒã‚¯
-Rubyã«é–¢ã™ã‚‹è³ªå•㯠Ruby-Talk(英語)や Ruby-List(日本語) (https://www.ruby-lang.org/ja/community/mailing-lists) や,
-stackoverflow (https://ja.stackoverflow.com/) ãªã©ã®Webã‚µã‚¤ãƒˆã«æŠ•ç¨¿ã—ã¦ãã ã•ã„.
+Rubyã«é–¢ã™ã‚‹è³ªå•㯠[Ruby-Talk](英語)や [Ruby-List](日本語)や,
+[stackoverflow] ãªã©ã®Webã‚µã‚¤ãƒˆã«æŠ•ç¨¿ã—ã¦ãã ã•ã„.
ãƒã‚°å ±å‘Šã¯ https://bugs.ruby-lang.org ã§å—ã‘付ã‘ã¦ã„ã¾ã™ï¼Ž
+[Ruby-Talk]: https://www.ruby-lang.org/en/community/mailing-lists
+[Ruby-List]: https://www.ruby-lang.org/ja/community/mailing-lists
+[stackoverflow]: https://ja.stackoverflow.com/
## 著者
Rubyã®ã‚ªãƒªã‚¸ãƒŠãƒ«ç‰ˆã¯ï¼Œ1995å¹´ã«ã¾ã¤ã‚‚ã¨ã‚†ãã²ã‚æ°ã«ã‚ˆã£ã¦è¨­è¨ˆãƒ»é–‹ç™ºã•れã¾ã—ãŸï¼Ž
<mailto:matz@ruby-lang.org>
-
----
-created at: Thu Aug 3 11:57:36 JST 1995
diff --git a/README.md b/README.md
index 099bc1435e..c445448c71 100644
--- a/README.md
+++ b/README.md
@@ -1,171 +1,91 @@
-[![Build Status](https://travis-ci.org/ruby/ruby.svg)](https://travis-ci.org/ruby/ruby)
-[![Build status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/trunk?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/trunk)
+[![Actions Status: MinGW](https://github.com/ruby/ruby/workflows/MinGW/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW")
+[![Actions Status: MJIT](https://github.com/ruby/ruby/workflows/MJIT/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"MJIT")
+[![Actions Status: Ubuntu](https://github.com/ruby/ruby/workflows/Ubuntu/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu")
+[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
+[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)
+[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)
-# What's Ruby
+# What is Ruby?
-Ruby is the interpreted scripting language for quick and easy object-oriented
-programming. It has many features to process text files and to do system
-management tasks (as in Perl). It is simple, straight-forward, and
-extensible.
+Ruby is an interpreted object-oriented programming language often
+used for web development. It also offers many scripting features
+to process plain text and serialized files, or manage system tasks.
+It is simple, straightforward, and extensible.
## Features of Ruby
-* Simple Syntax
-* **Normal** Object-oriented Features (e.g. class, method calls)
-* **Advanced** Object-oriented Features (e.g. mix-in, singleton-method)
-* Operator Overloading
-* Exception Handling
-* Iterators and Closures
-* Garbage Collection
-* Dynamic Loading of Object Files (on some architectures)
-* Highly Portable (works on many Unix-like/POSIX compatible platforms as
- well as Windows, macOS, Haiku, etc.) cf.
- https://github.com/ruby/ruby/blob/trunk/doc/contributing.rdoc#platform-maintainers
+* Simple Syntax
+* **Normal** Object-oriented Features (e.g. class, method calls)
+* **Advanced** Object-oriented Features (e.g. mix-in, singleton-method)
+* Operator Overloading
+* Exception Handling
+* Iterators and Closures
+* Garbage Collection
+* Dynamic Loading of Object Files (on some architectures)
+* Highly Portable (works on many Unix-like/POSIX compatible platforms as
+ well as Windows, macOS, etc.) cf.
+ https://github.com/ruby/ruby/blob/master/doc/maintainers.rdoc#label-Platform+Maintainers
-
-## How to get Ruby
+## How to get Ruby with Git
For a complete list of ways to install Ruby, including using third-party tools
like rvm, see:
https://www.ruby-lang.org/en/downloads/
-The trunk of the Ruby source tree can be checked out with the following
-command:
-
- $ svn co https://svn.ruby-lang.org/repos/ruby/trunk/ ruby
-
-Or if you are using git then use the following command:
+The mirror of the Ruby source tree can be checked out with the following command:
$ git clone https://github.com/ruby/ruby.git
There are some other branches under development. Try the following command
to see the list of branches:
- $ svn ls https://svn.ruby-lang.org/repos/ruby/branches/
+ $ git ls-remote https://github.com/ruby/ruby.git
-Or if you are using git then use the following command:
+You may also want to use https://git.ruby-lang.org/ruby.git (actual master of Ruby source)
+if you are a committer.
- $ git ls-remote https://github.com/ruby/ruby.git
+## How to build
-## Ruby home page
+see [Building Ruby](doc/contributing/building_ruby.md)
-The URL of the Ruby home page is:
+## Ruby home page
https://www.ruby-lang.org/
+## Documentation
+
+- [English](https://docs.ruby-lang.org/en/master/index.html)
+- [Japanese](https://docs.ruby-lang.org/ja/master/index.html)
+
## Mailing list
-There is a mailing list to talk about Ruby. To subscribe to this list, please
+There is a mailing list to discuss Ruby. To subscribe to this list, please
send the following phrase:
subscribe
-in the mail body (not subject) to the address
-<ruby-talk-request@ruby-lang.org>.
-
-## How to compile and install
-
-This is what you need to do to compile and install Ruby:
-
-1. If you want to use Microsoft Visual C++ to compile Ruby, read
- [win32/README.win32](win32/README.win32) instead of this document.
-
-2. If `./configure` does not exist or is older than `configure.ac`, run
- `autoconf` to (re)generate configure.
-
-3. Run `./configure`, which will generate `config.h` and `Makefile`.
-
- Some C compiler flags may be added by default depending on your
- environment. Specify `optflags=..` and `warnflags=..` as necessary to
- override them.
-
-4. Edit `defines.h` if you need. Usually this step will not be needed.
-
-5. Remove comment mark(`#`) before the module names from `ext/Setup` (or add
- module names if not present), if you want to link modules statically.
-
- If you don't want to compile non static extension modules (probably on
- architectures which do not allow dynamic loading), remove comment mark
- from the line "`#option nodynamic`" in `ext/Setup`.
+in the mail body (not subject) to the address [ruby-talk-request@ruby-lang.org].
- Usually this step will not be needed.
-
-6. Run `make`.
-
- * On Mac, set RUBY\_CODESIGN environment variable with a signing identity.
- It uses the identity to sign `ruby` binary. See also codesign(1).
-
-7. Optionally, run '`make check`' to check whether the compiled Ruby
- interpreter works well. If you see the message "`check succeeded`", your
- Ruby works as it should (hopefully).
-
-8. Optionally, run `make update-gems` and `make extract-gems`.
-
- If you want to install bundled gems, run `make update-gems` and
- `make extract-gems` before running `make install`.
-
-9. Run '`make install`'.
-
- This command will create the following directories and install files into
- them.
-
- * `${DESTDIR}${prefix}/bin`
- * `${DESTDIR}${prefix}/include/ruby-${MAJOR}.${MINOR}.${TEENY}`
- * `${DESTDIR}${prefix}/include/ruby-${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}`
- * `${DESTDIR}${prefix}/lib`
- * `${DESTDIR}${prefix}/lib/ruby`
- * `${DESTDIR}${prefix}/lib/ruby/${MAJOR}.${MINOR}.${TEENY}`
- * `${DESTDIR}${prefix}/lib/ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}`
- * `${DESTDIR}${prefix}/lib/ruby/site_ruby`
- * `${DESTDIR}${prefix}/lib/ruby/site_ruby/${MAJOR}.${MINOR}.${TEENY}`
- * `${DESTDIR}${prefix}/lib/ruby/site_ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}`
- * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby`
- * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby/${MAJOR}.${MINOR}.${TEENY}`
- * `${DESTDIR}${prefix}/lib/ruby/vendor_ruby/${MAJOR}.${MINOR}.${TEENY}/${PLATFORM}`
- * `${DESTDIR}${prefix}/lib/ruby/gems/${MAJOR}.${MINOR}.${TEENY}`
- * `${DESTDIR}${prefix}/share/man/man1`
- * `${DESTDIR}${prefix}/share/ri/${MAJOR}.${MINOR}.${TEENY}/system`
-
-
- If Ruby's API version is '*x.y.z*', the `${MAJOR}` is '*x*', the
- `${MINOR}` is '*y*', and the `${TEENY}` is '*z*'.
-
- **NOTE**: teeny of the API version may be different from one of Ruby's
- program version
-
- You may have to be a super user to install Ruby.
-
-
-If you fail to compile Ruby, please send the detailed error report with the
-error log and machine/OS type, to help others.
-
-Some extension libraries may not get compiled because of lack of necessary
-external libraries and/or headers, then you will need to run '`make distclean-ext`'
-to remove old configuration after installing them in such case.
+[ruby-talk-request@ruby-lang.org]: mailto:ruby-talk-request@ruby-lang.org?subject=Join%20Ruby%20Mailing%20List&body=subscribe
## Copying
-See the file [COPYING](COPYING).
+See the file [COPYING](rdoc-ref:COPYING).
## Feedback
-Questions about the Ruby language can be asked on the Ruby-Talk mailing list
-(https://www.ruby-lang.org/en/community/mailing-lists) or on websites like
-(https://stackoverflow.com).
+Questions about the Ruby language can be asked on the [Ruby-Talk](https://www.ruby-lang.org/en/community/mailing-lists) mailing list
+or on websites like https://stackoverflow.com.
-Bug reports should be filed at https://bugs.ruby-lang.org. Read [HowToReport] for more information.
-
-[HowToReport]: https://bugs.ruby-lang.org/projects/ruby/wiki/HowToReport
+Bugs should be reported at https://bugs.ruby-lang.org. Read ["Reporting Issues"](https://docs.ruby-lang.org/en/master/contributing/reporting_issues_md.html) for more information.
## Contributing
-See the file [CONTRIBUTING.md](CONTRIBUTING.md)
-
+See ["Contributing to Ruby"](https://docs.ruby-lang.org/en/master/contributing_md.html), which includes setup and build instructions.
## The Author
-Ruby was originally designed and developed by Yukihiro Matsumoto (Matz) in
-1995.
+Ruby was originally designed and developed by Yukihiro Matsumoto (Matz) in 1995.
<matz@ruby-lang.org>
diff --git a/aclocal.m4 b/aclocal.m4
index 223157abbb..e69de29bb2 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,46 +0,0 @@
-# generated automatically by aclocal 1.15.1 -*- Autoconf -*-
-
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
-
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
-m4_include([tool/m4/_colorize_result_prepare.m4])
-m4_include([tool/m4/ac_msg_result.m4])
-m4_include([tool/m4/colorize_result.m4])
-m4_include([tool/m4/ruby_append_option.m4])
-m4_include([tool/m4/ruby_append_options.m4])
-m4_include([tool/m4/ruby_check_builtin_func.m4])
-m4_include([tool/m4/ruby_check_builtin_setjmp.m4])
-m4_include([tool/m4/ruby_check_printf_prefix.m4])
-m4_include([tool/m4/ruby_check_setjmp.m4])
-m4_include([tool/m4/ruby_check_signedness.m4])
-m4_include([tool/m4/ruby_check_sizeof.m4])
-m4_include([tool/m4/ruby_check_sysconf.m4])
-m4_include([tool/m4/ruby_cppoutfile.m4])
-m4_include([tool/m4/ruby_decl_attribute.m4])
-m4_include([tool/m4/ruby_default_arch.m4])
-m4_include([tool/m4/ruby_define_if.m4])
-m4_include([tool/m4/ruby_defint.m4])
-m4_include([tool/m4/ruby_dtrace_available.m4])
-m4_include([tool/m4/ruby_dtrace_postprocess.m4])
-m4_include([tool/m4/ruby_func_attribute.m4])
-m4_include([tool/m4/ruby_mingw32.m4])
-m4_include([tool/m4/ruby_prepend_option.m4])
-m4_include([tool/m4/ruby_prog_gnu_ld.m4])
-m4_include([tool/m4/ruby_replace_type.m4])
-m4_include([tool/m4/ruby_rm_recursive.m4])
-m4_include([tool/m4/ruby_setjmp_type.m4])
-m4_include([tool/m4/ruby_stack_grow_direction.m4])
-m4_include([tool/m4/ruby_try_cflags.m4])
-m4_include([tool/m4/ruby_try_ldflags.m4])
-m4_include([tool/m4/ruby_type_attribute.m4])
-m4_include([tool/m4/ruby_universal_arch.m4])
-m4_include([tool/m4/ruby_werror_flag.m4])
diff --git a/addr2line.c b/addr2line.c
index 9220a0e0f6..e5f25293e2 100644
--- a/addr2line.c
+++ b/addr2line.c
@@ -8,7 +8,12 @@
**********************************************************************/
-#include "ruby/config.h"
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wgnu-empty-initializer"
+#pragma clang diagnostic ignored "-Wgcc-compat"
+#endif
+
+#include "ruby/internal/config.h"
#include "ruby/defines.h"
#include "ruby/missing.h"
#include "addr2line.h"
@@ -16,13 +21,13 @@
#include <stdio.h>
#include <errno.h>
-#ifdef HAVE_STDBOOL_H
-#include <stdbool.h>
-#else
-#include "missing/stdbool.h"
+#ifdef HAVE_LIBPROC_H
+#include <libproc.h>
#endif
-#ifdef USE_ELF
+#include "ruby/internal/stdbool.h"
+
+#if defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)
#include <fcntl.h>
#include <limits.h>
@@ -35,12 +40,6 @@
#include <sys/stat.h>
#include <unistd.h>
-#ifdef __OpenBSD__
-#include <elf_abi.h>
-#else
-#include <elf.h>
-#endif
-
/* Make alloca work the best possible way. */
#ifdef __GNUC__
# ifndef alloca
@@ -64,24 +63,20 @@ void *alloca();
# include <dlfcn.h>
#endif
-#define DW_LNS_copy 0x01
-#define DW_LNS_advance_pc 0x02
-#define DW_LNS_advance_line 0x03
-#define DW_LNS_set_file 0x04
-#define DW_LNS_set_column 0x05
-#define DW_LNS_negate_stmt 0x06
-#define DW_LNS_set_basic_block 0x07
-#define DW_LNS_const_add_pc 0x08
-#define DW_LNS_fixed_advance_pc 0x09
-#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */
-#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */
-#define DW_LNS_set_isa 0x0c /* DWARF3 */
+#ifdef HAVE_MACH_O_LOADER_H
+# include <crt_externs.h>
+# include <mach-o/fat.h>
+# include <mach-o/loader.h>
+# include <mach-o/nlist.h>
+# include <mach-o/stab.h>
+#endif
-/* Line number extended opcode name. */
-#define DW_LNE_end_sequence 0x01
-#define DW_LNE_set_address 0x02
-#define DW_LNE_define_file 0x03
-#define DW_LNE_set_discriminator 0x04 /* DWARF4 */
+#ifdef USE_ELF
+# ifdef __OpenBSD__
+# include <elf_abi.h>
+# else
+# include <elf.h>
+# endif
#ifndef ElfW
# if SIZEOF_VOIDP == 8
@@ -97,8 +92,6 @@ void *alloca();
# define ELF_ST_TYPE ELF32_ST_TYPE
# endif
#endif
-#ifndef PATH_MAX
-#define PATH_MAX 4096
#endif
#ifdef SHF_COMPRESSED
@@ -111,9 +104,32 @@ void *alloca();
# define SHF_COMPRESSED 0
#endif
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define DW_LNS_copy 0x01
+#define DW_LNS_advance_pc 0x02
+#define DW_LNS_advance_line 0x03
+#define DW_LNS_set_file 0x04
+#define DW_LNS_set_column 0x05
+#define DW_LNS_negate_stmt 0x06
+#define DW_LNS_set_basic_block 0x07
+#define DW_LNS_const_add_pc 0x08
+#define DW_LNS_fixed_advance_pc 0x09
+#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */
+#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */
+#define DW_LNS_set_isa 0x0c /* DWARF3 */
+
+/* Line number extended opcode name. */
+#define DW_LNE_end_sequence 0x01
+#define DW_LNE_set_address 0x02
+#define DW_LNE_define_file 0x03
+#define DW_LNE_set_discriminator 0x04 /* DWARF4 */
+
PRINTF_ARGS(static int kprintf(const char *fmt, ...), 1, 2);
-typedef struct {
+typedef struct line_info {
const char *dirname;
const char *filename;
const char *path; /* object path */
@@ -122,27 +138,72 @@ typedef struct {
uintptr_t base_addr;
uintptr_t saddr;
const char *sname; /* function name */
+
+ struct line_info *next;
} line_info_t;
-typedef struct obj_info obj_info_t;
-struct obj_info {
+
+struct dwarf_section {
+ char *ptr;
+ size_t size;
+ uint64_t flags;
+};
+
+typedef struct obj_info {
const char *path; /* object path */
- void *mapped;
+ char *mapped;
size_t mapped_size;
- void *uncompressed_debug_line;
+ void *uncompressed;
uintptr_t base_addr;
- obj_info_t *next;
+ uintptr_t vmaddr;
+ struct dwarf_section debug_abbrev;
+ struct dwarf_section debug_info;
+ struct dwarf_section debug_line;
+ struct dwarf_section debug_ranges;
+ struct dwarf_section debug_str_offsets;
+ struct dwarf_section debug_addr;
+ struct dwarf_section debug_rnglists;
+ struct dwarf_section debug_str;
+ struct dwarf_section debug_line_str;
+ struct obj_info *next;
+} obj_info_t;
+
+#define DWARF_SECTION_COUNT 9
+
+static struct dwarf_section *
+obj_dwarf_section_at(obj_info_t *obj, int n)
+{
+ struct dwarf_section *ary[] = {
+ &obj->debug_abbrev,
+ &obj->debug_info,
+ &obj->debug_line,
+ &obj->debug_ranges,
+ &obj->debug_str_offsets,
+ &obj->debug_addr,
+ &obj->debug_rnglists,
+ &obj->debug_str,
+ &obj->debug_line_str
+ };
+ if (n < 0 || DWARF_SECTION_COUNT <= n) {
+ abort();
+ }
+ return ary[n];
+}
+
+struct debug_section_definition {
+ const char *name;
+ struct dwarf_section *dwarf;
};
/* Avoid consuming stack as this module may be used from signal handler */
-static char binary_filename[PATH_MAX];
+static char binary_filename[PATH_MAX + 1];
static unsigned long
-uleb128(char **p)
+uleb128(const char **p)
{
unsigned long r = 0;
int s = 0;
for (;;) {
- unsigned char b = *(unsigned char *)(*p)++;
+ unsigned char b = (unsigned char)*(*p)++;
if (b < 0x80) {
r += (unsigned long)b << s;
break;
@@ -154,12 +215,12 @@ uleb128(char **p)
}
static long
-sleb128(char **p)
+sleb128(const char **p)
{
long r = 0;
int s = 0;
for (;;) {
- unsigned char b = *(unsigned char *)(*p)++;
+ unsigned char b = (unsigned char)*(*p)++;
if (b < 0x80) {
if (b & 0x40) {
r -= (0x80 - b) << s;
@@ -176,7 +237,7 @@ sleb128(char **p)
}
static const char *
-get_nth_dirname(unsigned long dir, char *p)
+get_nth_dirname(unsigned long dir, const char *p)
{
if (!dir--) {
return "";
@@ -193,141 +254,191 @@ get_nth_dirname(unsigned long dir, char *p)
return p;
}
+static const char *parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index);
+
static void
-fill_filename(int file, char *include_directories, char *filenames,
- line_info_t *line)
+fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories, const char *filenames, line_info_t *line, obj_info_t *obj)
{
int i;
- char *p = filenames;
- char *filename;
+ const char *p = filenames;
+ const char *filename;
unsigned long dir;
- for (i = 1; i <= file; i++) {
- filename = p;
- if (!*p) {
- /* Need to output binary file name? */
- kprintf("Unexpected file number %d in %s\n",
- file, binary_filename);
- return;
- }
- while (*p) p++;
- p++;
- dir = uleb128(&p);
- /* last modified. */
- uleb128(&p);
- /* size of the file. */
- uleb128(&p);
-
- if (i == file) {
- line->filename = filename;
- line->dirname = get_nth_dirname(dir, include_directories);
- }
+ if (version >= 5) {
+ const char *path;
+ uint64_t directory_index = -1;
+ parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index);
+ line->filename = path;
+ parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL);
+ line->dirname = path;
+ }
+ else {
+ for (i = 1; i <= file; i++) {
+ filename = p;
+ if (!*p) {
+ /* Need to output binary file name? */
+ kprintf("Unexpected file number %d in %s at %tx\n",
+ file, binary_filename, filenames - obj->mapped);
+ return;
+ }
+ while (*p) p++;
+ p++;
+ dir = uleb128(&p);
+ /* last modified. */
+ uleb128(&p);
+ /* size of the file. */
+ uleb128(&p);
+
+ if (i == file) {
+ line->filename = filename;
+ line->dirname = get_nth_dirname(dir, include_directories);
+ }
+ }
}
}
static void
fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
- char *include_directories, char *filenames,
+ uint8_t format, uint16_t version, const char *include_directories, const char *filenames,
obj_info_t *obj, line_info_t *lines, int offset)
{
int i;
- addr += obj->base_addr;
+ addr += obj->base_addr - obj->vmaddr;
for (i = offset; i < num_traces; i++) {
uintptr_t a = (uintptr_t)traces[i];
/* We assume one line code doesn't result >100 bytes of native code.
We may want more reliable way eventually... */
if (addr < a && a < addr + 100) {
- fill_filename(file, include_directories, filenames, &lines[i]);
+ fill_filename(file, format, version, include_directories, filenames, &lines[i], obj);
lines[i].line = line;
}
}
}
+struct LineNumberProgramHeader {
+ uint64_t unit_length;
+ uint16_t version;
+ uint8_t format; /* 4 or 8 */
+ uint64_t header_length;
+ uint8_t minimum_instruction_length;
+ uint8_t maximum_operations_per_instruction;
+ uint8_t default_is_stmt;
+ int8_t line_base;
+ uint8_t line_range;
+ uint8_t opcode_base;
+ /* uint8_t standard_opcode_lengths[opcode_base-1]; */
+ const char *include_directories;
+ const char *filenames;
+ const char *cu_start;
+ const char *cu_end;
+};
+
static int
-parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
- obj_info_t *obj, line_info_t *lines, int offset)
+parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header)
{
- char *p, *cu_end, *cu_start, *include_directories, *filenames;
- unsigned long unit_length;
- int default_is_stmt, line_base;
- unsigned int header_length, minimum_instruction_length, line_range,
- opcode_base;
- /* unsigned char *standard_opcode_lengths; */
-
- /* The registers. */
- unsigned long addr = 0;
- unsigned int file = 1;
- unsigned int line = 1;
- /* unsigned int column = 0; */
- int is_stmt;
- /* int basic_block = 0; */
- /* int end_sequence = 0; */
- /* int prologue_end = 0; */
- /* int epilogue_begin = 0; */
- /* unsigned int isa = 0; */
-
- p = *debug_line;
-
- unit_length = *(unsigned int *)p;
- p += sizeof(unsigned int);
- if (unit_length == 0xffffffff) {
- unit_length = *(unsigned long *)p;
- p += sizeof(unsigned long);
+ const char *p = *pp;
+ header->unit_length = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+
+ header->format = 4;
+ if (header->unit_length == 0xffffffff) {
+ header->unit_length = *(uint64_t *)p;
+ p += sizeof(uint64_t);
+ header->format = 8;
}
- cu_end = p + unit_length;
+ header->cu_end = p + header->unit_length;
- /*dwarf_version = *(unsigned short *)p;*/
- p += 2;
+ header->version = *(uint16_t *)p;
+ p += sizeof(uint16_t);
+ if (header->version > 5) return -1;
- header_length = *(unsigned int *)p;
- p += sizeof(unsigned int);
+ if (header->version >= 5) {
+ /* address_size = *(uint8_t *)p++; */
+ /* segment_selector_size = *(uint8_t *)p++; */
+ p += 2;
+ }
- cu_start = p + header_length;
+ header->header_length = header->format == 4 ? *(uint32_t *)p : *(uint64_t *)p;
+ p += header->format;
+ header->cu_start = p + header->header_length;
- minimum_instruction_length = *(unsigned char *)p;
- p++;
+ header->minimum_instruction_length = *(uint8_t *)p++;
- is_stmt = default_is_stmt = *(unsigned char *)p;
- p++;
+ if (header->version >= 4) {
+ /* maximum_operations_per_instruction = *(uint8_t *)p; */
+ if (*p != 1) return -1; /* For non-VLIW architectures, this field is 1 */
+ p++;
+ }
- line_base = *(signed char *)p;
- p++;
+ header->default_is_stmt = *(uint8_t *)p++;
+ header->line_base = *(int8_t *)p++;
+ header->line_range = *(uint8_t *)p++;
+ header->opcode_base = *(uint8_t *)p++;
+ /* header->standard_opcode_lengths = (uint8_t *)p - 1; */
+ p += header->opcode_base - 1;
+
+ if (header->version >= 5) {
+ header->include_directories = p;
+ p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL);
+ header->filenames = p;
+ }
+ else {
+ header->include_directories = p;
- line_range = *(unsigned char *)p;
- p++;
+ /* temporary measure for compress-debug-sections */
+ if (p >= header->cu_end) return -1;
- opcode_base = *(unsigned char *)p;
- p++;
+ /* skip include directories */
+ while (*p) {
+ p = memchr(p, '\0', header->cu_end - p);
+ if (!p) return -1;
+ p++;
+ }
+ p++;
- /* standard_opcode_lengths = (unsigned char *)p - 1; */
- p += opcode_base - 1;
+ header->filenames = p;
+ }
- include_directories = p;
+ *pp = header->cu_start;
- /* temporary measure for compress-debug-sections */
- if (p >= cu_end) return -1;
+ return 0;
+}
- /* skip include directories */
- while (*p) {
- p = memchr(p, '\0', cu_end - p);
- if (!p) return -1;
- p++;
- }
- p++;
+static int
+parse_debug_line_cu(int num_traces, void **traces, const char **debug_line,
+ obj_info_t *obj, line_info_t *lines, int offset)
+{
+ const char *p = (const char *)*debug_line;
+ struct LineNumberProgramHeader header;
- filenames = p;
+ /* The registers. */
+ unsigned long addr = 0;
+ unsigned int file = 1;
+ unsigned int line = 1;
+ /* unsigned int column = 0; */
+ int is_stmt;
+ /* int basic_block = 0; */
+ /* int end_sequence = 0; */
+ /* int prologue_end = 0; */
+ /* int epilogue_begin = 0; */
+ /* unsigned int isa = 0; */
- p = cu_start;
+ if (parse_debug_line_header(obj, &p, &header))
+ return -1;
+ is_stmt = header.default_is_stmt;
#define FILL_LINE() \
do { \
fill_line(num_traces, traces, addr, file, line, \
- include_directories, filenames, \
+ header.format, \
+ header.version, \
+ header.include_directories, \
+ header.filenames, \
obj, lines, offset); \
/*basic_block = prologue_end = epilogue_begin = 0;*/ \
} while (0)
- while (p < cu_end) {
+ while (p < header.cu_end) {
unsigned long a;
unsigned char op = *p++;
switch (op) {
@@ -335,7 +446,7 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
FILL_LINE();
break;
case DW_LNS_advance_pc:
- a = uleb128(&p);
+ a = uleb128(&p) * header.minimum_instruction_length;
addr += a;
break;
case DW_LNS_advance_line: {
@@ -356,12 +467,13 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
/*basic_block = 1; */
break;
case DW_LNS_const_add_pc:
- a = ((255 - opcode_base) / line_range) *
- minimum_instruction_length;
+ a = ((255UL - header.opcode_base) / header.line_range) *
+ header.minimum_instruction_length;
addr += a;
break;
case DW_LNS_fixed_advance_pc:
- a = *(unsigned char *)p++;
+ a = *(uint16_t *)p;
+ p += sizeof(uint16_t);
addr += a;
break;
case DW_LNS_set_prologue_end:
@@ -374,7 +486,7 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
/* isa = (unsigned int)*/(void)uleb128(&p);
break;
case 0:
- a = *(unsigned char *)p++;
+ a = uleb128(&p);
op = *p++;
switch (op) {
case DW_LNE_end_sequence:
@@ -384,7 +496,7 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
file = 1;
line = 1;
/* column = 0; */
- is_stmt = default_is_stmt;
+ is_stmt = header.default_is_stmt;
/* end_sequence = 0; */
/* isa = 0; */
break;
@@ -406,27 +518,25 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line,
}
break;
default: {
- unsigned long addr_incr;
- unsigned long line_incr;
- a = op - opcode_base;
- addr_incr = (a / line_range) * minimum_instruction_length;
- line_incr = line_base + (a % line_range);
- addr += (unsigned int)addr_incr;
- line += (unsigned int)line_incr;
+ uint8_t adjusted_opcode = op - header.opcode_base;
+ uint8_t operation_advance = adjusted_opcode / header.line_range;
+ /* NOTE: this code doesn't support VLIW */
+ addr += operation_advance * header.minimum_instruction_length;
+ line += header.line_base + (adjusted_opcode % header.line_range);
FILL_LINE();
}
}
}
- *debug_line = p;
+ *debug_line = (char *)p;
return 0;
}
static int
parse_debug_line(int num_traces, void **traces,
- char *debug_line, unsigned long size,
+ const char *debug_line, unsigned long size,
obj_info_t *obj, line_info_t *lines, int offset)
{
- char *debug_line_end = debug_line + size;
+ const char *debug_line_end = debug_line + size;
while (debug_line < debug_line_end) {
if (parse_debug_line_cu(num_traces, traces, &debug_line, obj, lines, offset))
return -1;
@@ -451,13 +561,26 @@ append_obj(obj_info_t **objp)
*objp = newobj;
}
+#ifdef USE_ELF
+/* Ideally we should check 4 paths to follow gnu_debuglink:
+ *
+ * - /usr/lib/debug/.build-id/ab/cdef1234.debug
+ * - /usr/bin/ruby.debug
+ * - /usr/bin/.debug/ruby.debug
+ * - /usr/lib/debug/usr/bin/ruby.debug.
+ *
+ * but we handle only two cases for now as the two formats are
+ * used by some linux distributions.
+ *
+ * See GDB's info for detail.
+ * https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
+ */
+
+// check the path pattern of "/usr/lib/debug/usr/bin/ruby.debug"
static void
follow_debuglink(const char *debuglink, int num_traces, void **traces,
obj_info_t **objp, line_info_t *lines, int offset)
{
- /* Ideally we should check 4 paths to follow gnu_debuglink,
- but we handle only one case for now as this format is used
- by some linux distributions. See GDB's info for detail. */
static const char global_debug_dir[] = "/usr/lib/debug";
const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1;
char *p;
@@ -485,40 +608,1397 @@ follow_debuglink(const char *debuglink, int num_traces, void **traces,
fill_lines(num_traces, traces, 0, objp, lines, offset);
}
-#ifdef SUPPORT_COMPRESSED_DEBUG_LINE
+// check the path pattern of "/usr/lib/debug/.build-id/ab/cdef1234.debug"
+static void
+follow_debuglink_build_id(const char *build_id, size_t build_id_size, int num_traces, void **traces,
+ obj_info_t **objp, line_info_t *lines, int offset)
+{
+ static const char global_debug_dir[] = "/usr/lib/debug/.build-id/";
+ const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1;
+ char *p;
+ obj_info_t *o1 = *objp, *o2;
+ size_t i;
+
+ if (PATH_MAX < global_debug_dir_len + 1 + build_id_size * 2 + 6) return;
+
+ memcpy(binary_filename, global_debug_dir, global_debug_dir_len);
+ p = binary_filename + global_debug_dir_len;
+ for (i = 0; i < build_id_size; i++) {
+ static const char tbl[] = "0123456789abcdef";
+ unsigned char n = build_id[i];
+ *p++ = tbl[n / 16];
+ *p++ = tbl[n % 16];
+ if (i == 0) *p++ = '/';
+ }
+ strcpy(p, ".debug");
+
+ append_obj(objp);
+ o2 = *objp;
+ o2->base_addr = o1->base_addr;
+ o2->path = o1->path;
+ fill_lines(num_traces, traces, 0, objp, lines, offset);
+}
+#endif
+
+enum
+{
+ DW_TAG_compile_unit = 0x11,
+ DW_TAG_inlined_subroutine = 0x1d,
+ DW_TAG_subprogram = 0x2e,
+};
+
+/* Attributes encodings */
+enum
+{
+ DW_AT_sibling = 0x01,
+ DW_AT_location = 0x02,
+ DW_AT_name = 0x03,
+ /* Reserved 0x04 */
+ /* Reserved 0x05 */
+ /* Reserved 0x06 */
+ /* Reserved 0x07 */
+ /* Reserved 0x08 */
+ DW_AT_ordering = 0x09,
+ /* Reserved 0x0a */
+ DW_AT_byte_size = 0x0b,
+ /* Reserved 0x0c */
+ DW_AT_bit_size = 0x0d,
+ /* Reserved 0x0e */
+ /* Reserved 0x0f */
+ DW_AT_stmt_list = 0x10,
+ DW_AT_low_pc = 0x11,
+ DW_AT_high_pc = 0x12,
+ DW_AT_language = 0x13,
+ /* Reserved 0x14 */
+ DW_AT_discr = 0x15,
+ DW_AT_discr_value = 0x16,
+ DW_AT_visibility = 0x17,
+ DW_AT_import = 0x18,
+ DW_AT_string_length = 0x19,
+ DW_AT_common_reference = 0x1a,
+ DW_AT_comp_dir = 0x1b,
+ DW_AT_const_value = 0x1c,
+ DW_AT_containing_type = 0x1d,
+ DW_AT_default_value = 0x1e,
+ /* Reserved 0x1f */
+ DW_AT_inline = 0x20,
+ DW_AT_is_optional = 0x21,
+ DW_AT_lower_bound = 0x22,
+ /* Reserved 0x23 */
+ /* Reserved 0x24 */
+ DW_AT_producer = 0x25,
+ /* Reserved 0x26 */
+ DW_AT_prototyped = 0x27,
+ /* Reserved 0x28 */
+ /* Reserved 0x29 */
+ DW_AT_return_addr = 0x2a,
+ /* Reserved 0x2b */
+ DW_AT_start_scope = 0x2c,
+ /* Reserved 0x2d */
+ DW_AT_bit_stride = 0x2e,
+ DW_AT_upper_bound = 0x2f,
+ /* Reserved 0x30 */
+ DW_AT_abstract_origin = 0x31,
+ DW_AT_accessibility = 0x32,
+ DW_AT_address_class = 0x33,
+ DW_AT_artificial = 0x34,
+ DW_AT_base_types = 0x35,
+ DW_AT_calling_convention = 0x36,
+ DW_AT_count = 0x37,
+ DW_AT_data_member_location = 0x38,
+ DW_AT_decl_column = 0x39,
+ DW_AT_decl_file = 0x3a,
+ DW_AT_decl_line = 0x3b,
+ DW_AT_declaration = 0x3c,
+ DW_AT_discr_list = 0x3d,
+ DW_AT_encoding = 0x3e,
+ DW_AT_external = 0x3f,
+ DW_AT_frame_base = 0x40,
+ DW_AT_friend = 0x41,
+ DW_AT_identifier_case = 0x42,
+ /* Reserved 0x43 */
+ DW_AT_namelist_item = 0x44,
+ DW_AT_priority = 0x45,
+ DW_AT_segment = 0x46,
+ DW_AT_specification = 0x47,
+ DW_AT_static_link = 0x48,
+ DW_AT_type = 0x49,
+ DW_AT_use_location = 0x4a,
+ DW_AT_variable_parameter = 0x4b,
+ DW_AT_virtuality = 0x4c,
+ DW_AT_vtable_elem_location = 0x4d,
+ DW_AT_allocated = 0x4e,
+ DW_AT_associated = 0x4f,
+ DW_AT_data_location = 0x50,
+ DW_AT_byte_stride = 0x51,
+ DW_AT_entry_pc = 0x52,
+ DW_AT_use_UTF8 = 0x53,
+ DW_AT_extension = 0x54,
+ DW_AT_ranges = 0x55,
+ DW_AT_trampoline = 0x56,
+ DW_AT_call_column = 0x57,
+ DW_AT_call_file = 0x58,
+ DW_AT_call_line = 0x59,
+ DW_AT_description = 0x5a,
+ DW_AT_binary_scale = 0x5b,
+ DW_AT_decimal_scale = 0x5c,
+ DW_AT_small = 0x5d,
+ DW_AT_decimal_sign = 0x5e,
+ DW_AT_digit_count = 0x5f,
+ DW_AT_picture_string = 0x60,
+ DW_AT_mutable = 0x61,
+ DW_AT_threads_scaled = 0x62,
+ DW_AT_explicit = 0x63,
+ DW_AT_object_pointer = 0x64,
+ DW_AT_endianity = 0x65,
+ DW_AT_elemental = 0x66,
+ DW_AT_pure = 0x67,
+ DW_AT_recursive = 0x68,
+ DW_AT_signature = 0x69,
+ DW_AT_main_subprogram = 0x6a,
+ DW_AT_data_bit_offset = 0x6b,
+ DW_AT_const_expr = 0x6c,
+ DW_AT_enum_class = 0x6d,
+ DW_AT_linkage_name = 0x6e,
+ DW_AT_string_length_bit_size = 0x6f,
+ DW_AT_string_length_byte_size = 0x70,
+ DW_AT_rank = 0x71,
+ DW_AT_str_offsets_base = 0x72,
+ DW_AT_addr_base = 0x73,
+ DW_AT_rnglists_base = 0x74,
+ /* Reserved 0x75 */
+ DW_AT_dwo_name = 0x76,
+ DW_AT_reference = 0x77,
+ DW_AT_rvalue_reference = 0x78,
+ DW_AT_macros = 0x79,
+ DW_AT_call_all_calls = 0x7a,
+ DW_AT_call_all_source_calls = 0x7b,
+ DW_AT_call_all_tail_calls = 0x7c,
+ DW_AT_call_return_pc = 0x7d,
+ DW_AT_call_value = 0x7e,
+ DW_AT_call_origin = 0x7f,
+ DW_AT_call_parameter = 0x80,
+ DW_AT_call_pc = 0x81,
+ DW_AT_call_tail_call = 0x82,
+ DW_AT_call_target = 0x83,
+ DW_AT_call_target_clobbered = 0x84,
+ DW_AT_call_data_location = 0x85,
+ DW_AT_call_data_value = 0x86,
+ DW_AT_noreturn = 0x87,
+ DW_AT_alignment = 0x88,
+ DW_AT_export_symbols = 0x89,
+ DW_AT_deleted = 0x8a,
+ DW_AT_defaulted = 0x8b,
+ DW_AT_loclists_base = 0x8c,
+ DW_AT_lo_user = 0x2000,
+ DW_AT_hi_user = 0x3fff
+};
+
+/* Attribute form encodings */
+enum
+{
+ DW_FORM_addr = 0x01,
+ /* Reserved 0x02 */
+ DW_FORM_block2 = 0x03,
+ DW_FORM_block4 = 0x04,
+ DW_FORM_data2 = 0x05,
+ DW_FORM_data4 = 0x06,
+ DW_FORM_data8 = 0x07,
+ DW_FORM_string = 0x08,
+ DW_FORM_block = 0x09,
+ DW_FORM_block1 = 0x0a,
+ DW_FORM_data1 = 0x0b,
+ DW_FORM_flag = 0x0c,
+ DW_FORM_sdata = 0x0d,
+ DW_FORM_strp = 0x0e,
+ DW_FORM_udata = 0x0f,
+ DW_FORM_ref_addr = 0x10,
+ DW_FORM_ref1 = 0x11,
+ DW_FORM_ref2 = 0x12,
+ DW_FORM_ref4 = 0x13,
+ DW_FORM_ref8 = 0x14,
+ DW_FORM_ref_udata = 0x15,
+ DW_FORM_indirect = 0x16,
+ DW_FORM_sec_offset = 0x17,
+ DW_FORM_exprloc = 0x18,
+ DW_FORM_flag_present = 0x19,
+ DW_FORM_strx = 0x1a,
+ DW_FORM_addrx = 0x1b,
+ DW_FORM_ref_sup4 = 0x1c,
+ DW_FORM_strp_sup = 0x1d,
+ DW_FORM_data16 = 0x1e,
+ DW_FORM_line_strp = 0x1f,
+ DW_FORM_ref_sig8 = 0x20,
+ DW_FORM_implicit_const = 0x21,
+ DW_FORM_loclistx = 0x22,
+ DW_FORM_rnglistx = 0x23,
+ DW_FORM_ref_sup8 = 0x24,
+ DW_FORM_strx1 = 0x25,
+ DW_FORM_strx2 = 0x26,
+ DW_FORM_strx3 = 0x27,
+ DW_FORM_strx4 = 0x28,
+ DW_FORM_addrx1 = 0x29,
+ DW_FORM_addrx2 = 0x2a,
+ DW_FORM_addrx3 = 0x2b,
+ DW_FORM_addrx4 = 0x2c
+};
+
+/* Range list entry encodings */
+enum {
+ DW_RLE_end_of_list = 0x00,
+ DW_RLE_base_addressx = 0x01,
+ DW_RLE_startx_endx = 0x02,
+ DW_RLE_startx_length = 0x03,
+ DW_RLE_offset_pair = 0x04,
+ DW_RLE_base_address = 0x05,
+ DW_RLE_start_end = 0x06,
+ DW_RLE_start_length = 0x07
+};
+
+enum {
+ VAL_none = 0,
+ VAL_cstr = 1,
+ VAL_data = 2,
+ VAL_uint = 3,
+ VAL_int = 4,
+ VAL_addr = 5
+};
+
+# define ABBREV_TABLE_SIZE 256
+typedef struct {
+ obj_info_t *obj;
+ const char *file;
+ uint8_t current_version;
+ const char *current_cu;
+ uint64_t current_low_pc;
+ uint64_t current_str_offsets_base;
+ uint64_t current_addr_base;
+ uint64_t current_rnglists_base;
+ const char *debug_line_cu_end;
+ uint8_t debug_line_format;
+ uint16_t debug_line_version;
+ const char *debug_line_files;
+ const char *debug_line_directories;
+ const char *p;
+ const char *cu_end;
+ const char *pend;
+ const char *q0;
+ const char *q;
+ int format; // 4 or 8
+ uint8_t address_size;
+ int level;
+ const char *abbrev_table[ABBREV_TABLE_SIZE];
+} DebugInfoReader;
+
+typedef struct {
+ ptrdiff_t pos;
+ int tag;
+ int has_children;
+} DIE;
+
+typedef struct {
+ union {
+ const char *ptr;
+ uint64_t uint64;
+ int64_t int64;
+ uint64_t addr_idx;
+ } as;
+ uint64_t off;
+ uint64_t at;
+ uint64_t form;
+ size_t size;
+ int type;
+} DebugInfoValue;
+
+#if defined(WORDS_BIGENDIAN)
+#define MERGE_2INTS(a,b,sz) (((uint64_t)(a)<<sz)|(b))
+#else
+#define MERGE_2INTS(a,b,sz) (((uint64_t)(b)<<sz)|(a))
+#endif
+
+static uint16_t
+get_uint16(const uint8_t *p)
+{
+ return (uint16_t)MERGE_2INTS(p[0],p[1],8);
+}
+
+static uint32_t
+get_uint32(const uint8_t *p)
+{
+ return (uint32_t)MERGE_2INTS(get_uint16(p),get_uint16(p+2),16);
+}
+
+static uint64_t
+get_uint64(const uint8_t *p)
+{
+ return MERGE_2INTS(get_uint32(p),get_uint32(p+4),32);
+}
+
+static uint8_t
+read_uint8(const char **ptr)
+{
+ const char *p = *ptr;
+ *ptr = (p + 1);
+ return (uint8_t)*p;
+}
+
+static uint16_t
+read_uint16(const char **ptr)
+{
+ const char *p = *ptr;
+ *ptr = (p + 2);
+ return get_uint16((const uint8_t *)p);
+}
+
+static uint32_t
+read_uint24(const char **ptr)
+{
+ const char *p = *ptr;
+ *ptr = (p + 3);
+ return ((uint8_t)*p << 16) | get_uint16((const uint8_t *)p+1);
+}
+
+static uint32_t
+read_uint32(const char **ptr)
+{
+ const char *p = *ptr;
+ *ptr = (p + 4);
+ return get_uint32((const uint8_t *)p);
+}
+
+static uint64_t
+read_uint64(const char **ptr)
+{
+ const unsigned char *p = (const unsigned char *)*ptr;
+ *ptr = (char *)(p + 8);
+ return get_uint64(p);
+}
+
+static uintptr_t
+read_uintptr(const char **ptr)
+{
+ const unsigned char *p = (const unsigned char *)*ptr;
+ *ptr = (char *)(p + SIZEOF_VOIDP);
+#if SIZEOF_VOIDP == 8
+ return get_uint64(p);
+#else
+ return get_uint32(p);
+#endif
+}
+
+static uint64_t
+read_uint(DebugInfoReader *reader)
+{
+ if (reader->format == 4) {
+ return read_uint32(&reader->p);
+ } else { /* 64 bit */
+ return read_uint64(&reader->p);
+ }
+}
+
+static uint64_t
+read_uleb128(DebugInfoReader *reader)
+{
+ return uleb128(&reader->p);
+}
+
+static int64_t
+read_sleb128(DebugInfoReader *reader)
+{
+ return sleb128(&reader->p);
+}
+
+static void
+debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj)
+{
+ reader->file = obj->mapped;
+ reader->obj = obj;
+ reader->p = obj->debug_info.ptr;
+ reader->pend = obj->debug_info.ptr + obj->debug_info.size;
+ reader->debug_line_cu_end = obj->debug_line.ptr;
+ reader->current_low_pc = 0;
+ reader->current_str_offsets_base = 0;
+ reader->current_addr_base = 0;
+ reader->current_rnglists_base = 0;
+}
+
+static void
+di_skip_die_attributes(const char **p)
+{
+ for (;;) {
+ uint64_t at = uleb128(p);
+ uint64_t form = uleb128(p);
+ if (!at && !form) break;
+ switch (form) {
+ default:
+ break;
+ case DW_FORM_implicit_const:
+ sleb128(p);
+ break;
+ }
+ }
+}
+
+static void
+di_read_debug_abbrev_cu(DebugInfoReader *reader)
+{
+ uint64_t prev = 0;
+ const char *p = reader->q0;
+ for (;;) {
+ uint64_t abbrev_number = uleb128(&p);
+ if (abbrev_number <= prev) break;
+ if (abbrev_number < ABBREV_TABLE_SIZE) {
+ reader->abbrev_table[abbrev_number] = p;
+ }
+ prev = abbrev_number;
+ uleb128(&p); /* tag */
+ p++; /* has_children */
+ di_skip_die_attributes(&p);
+ }
+}
+
static int
-parse_compressed_debug_line(int num_traces, void **traces,
- char *debug_line, unsigned long size,
- obj_info_t *obj, line_info_t *lines, int offset)
+di_read_debug_line_cu(DebugInfoReader *reader)
+{
+ const char *p;
+ struct LineNumberProgramHeader header;
+
+ p = (const char *)reader->debug_line_cu_end;
+ if (parse_debug_line_header(reader->obj, &p, &header))
+ return -1;
+
+ reader->debug_line_cu_end = (char *)header.cu_end;
+ reader->debug_line_format = header.format;
+ reader->debug_line_version = header.version;
+ reader->debug_line_directories = (char *)header.include_directories;
+ reader->debug_line_files = (char *)header.filenames;
+
+ return 0;
+}
+
+static void
+set_addr_idx_value(DebugInfoValue *v, uint64_t n)
+{
+ v->as.addr_idx = n;
+ v->type = VAL_addr;
+}
+
+static void
+set_uint_value(DebugInfoValue *v, uint64_t n)
+{
+ v->as.uint64 = n;
+ v->type = VAL_uint;
+}
+
+static void
+set_int_value(DebugInfoValue *v, int64_t n)
+{
+ v->as.int64 = n;
+ v->type = VAL_int;
+}
+
+static void
+set_cstr_value(DebugInfoValue *v, const char *s)
+{
+ v->as.ptr = s;
+ v->off = 0;
+ v->type = VAL_cstr;
+}
+
+static void
+set_cstrp_value(DebugInfoValue *v, const char *s, uint64_t off)
+{
+ v->as.ptr = s;
+ v->off = off;
+ v->type = VAL_cstr;
+}
+
+static void
+set_data_value(DebugInfoValue *v, const char *s)
+{
+ v->as.ptr = s;
+ v->type = VAL_data;
+}
+
+static const char *
+get_cstr_value(DebugInfoValue *v)
+{
+ if (v->as.ptr) {
+ return v->as.ptr + v->off;
+ } else {
+ return NULL;
+ }
+}
+
+static const char *
+resolve_strx(DebugInfoReader *reader, uint64_t idx)
+{
+ const char *p = reader->obj->debug_str_offsets.ptr + reader->current_str_offsets_base;
+ uint64_t off;
+ if (reader->format == 4) {
+ off = ((uint32_t *)p)[idx];
+ }
+ else {
+ off = ((uint64_t *)p)[idx];
+ }
+ return reader->obj->debug_str.ptr + off;
+}
+
+static void
+debug_info_reader_read_addr_value(DebugInfoReader *reader, DebugInfoValue *v)
+{
+ if (reader->address_size == 4) {
+ set_uint_value(v, read_uint32(&reader->p));
+ } else if (reader->address_size == 8) {
+ set_uint_value(v, read_uint64(&reader->p));
+ } else {
+ fprintf(stderr,"unknown address_size:%d", reader->address_size);
+ abort();
+ }
+}
+
+static void
+debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v)
{
- void *uncompressed_debug_line;
- ElfW(Chdr) *chdr = (ElfW(Chdr) *)debug_line;
+ switch (form) {
+ case DW_FORM_addr:
+ debug_info_reader_read_addr_value(reader, v);
+ break;
+ case DW_FORM_block2:
+ v->size = read_uint16(&reader->p);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_block4:
+ v->size = read_uint32(&reader->p);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_data2:
+ set_uint_value(v, read_uint16(&reader->p));
+ break;
+ case DW_FORM_data4:
+ set_uint_value(v, read_uint32(&reader->p));
+ break;
+ case DW_FORM_data8:
+ set_uint_value(v, read_uint64(&reader->p));
+ break;
+ case DW_FORM_string:
+ v->size = strlen(reader->p);
+ set_cstr_value(v, reader->p);
+ reader->p += v->size + 1;
+ break;
+ case DW_FORM_block:
+ v->size = uleb128(&reader->p);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_block1:
+ v->size = read_uint8(&reader->p);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_data1:
+ set_uint_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_flag:
+ set_uint_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_sdata:
+ set_int_value(v, read_sleb128(reader));
+ break;
+ case DW_FORM_strp:
+ set_cstrp_value(v, reader->obj->debug_str.ptr, read_uint(reader));
+ break;
+ case DW_FORM_udata:
+ set_uint_value(v, read_uleb128(reader));
+ break;
+ case DW_FORM_ref_addr:
+ if (reader->current_version <= 2) {
+ // DWARF Version 2 specifies that references have
+ // the same size as an address on the target system
+ debug_info_reader_read_addr_value(reader, v);
+ } else {
+ if (reader->format == 4) {
+ set_uint_value(v, read_uint32(&reader->p));
+ } else if (reader->format == 8) {
+ set_uint_value(v, read_uint64(&reader->p));
+ } else {
+ fprintf(stderr,"unknown format:%d", reader->format);
+ abort();
+ }
+ }
+ break;
+ case DW_FORM_ref1:
+ set_uint_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_ref2:
+ set_uint_value(v, read_uint16(&reader->p));
+ break;
+ case DW_FORM_ref4:
+ set_uint_value(v, read_uint32(&reader->p));
+ break;
+ case DW_FORM_ref8:
+ set_uint_value(v, read_uint64(&reader->p));
+ break;
+ case DW_FORM_ref_udata:
+ set_uint_value(v, uleb128(&reader->p));
+ break;
+ case DW_FORM_indirect:
+ /* TODO: read the referred value */
+ set_uint_value(v, uleb128(&reader->p));
+ break;
+ case DW_FORM_sec_offset:
+ set_uint_value(v, read_uint(reader)); /* offset */
+ /* addrptr: debug_addr */
+ /* lineptr: debug_line */
+ /* loclist: debug_loclists */
+ /* loclistptr: debug_loclists */
+ /* macptr: debug_macro */
+ /* rnglist: debug_rnglists */
+ /* rnglistptr: debug_rnglists */
+ /* stroffsetsptr: debug_str_offsets */
+ break;
+ case DW_FORM_exprloc:
+ v->size = (size_t)read_uleb128(reader);
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_flag_present:
+ set_uint_value(v, 1);
+ break;
+ case DW_FORM_strx:
+ set_cstr_value(v, resolve_strx(reader, uleb128(&reader->p)));
+ break;
+ case DW_FORM_addrx:
+ set_addr_idx_value(v, uleb128(&reader->p));
+ break;
+ case DW_FORM_ref_sup4:
+ set_uint_value(v, read_uint32(&reader->p));
+ break;
+ case DW_FORM_strp_sup:
+ set_uint_value(v, read_uint(reader));
+ /* *p = reader->sup_file + reader->sup_str->sh_offset + ret; */
+ break;
+ case DW_FORM_data16:
+ v->size = 16;
+ set_data_value(v, reader->p);
+ reader->p += v->size;
+ break;
+ case DW_FORM_line_strp:
+ set_cstrp_value(v, reader->obj->debug_line_str.ptr, read_uint(reader));
+ break;
+ case DW_FORM_ref_sig8:
+ set_uint_value(v, read_uint64(&reader->p));
+ break;
+ case DW_FORM_implicit_const:
+ set_int_value(v, sleb128(&reader->q));
+ break;
+ case DW_FORM_loclistx:
+ set_uint_value(v, read_uleb128(reader));
+ break;
+ case DW_FORM_rnglistx:
+ set_uint_value(v, read_uleb128(reader));
+ break;
+ case DW_FORM_ref_sup8:
+ set_uint_value(v, read_uint64(&reader->p));
+ break;
+ case DW_FORM_strx1:
+ set_cstr_value(v, resolve_strx(reader, read_uint8(&reader->p)));
+ break;
+ case DW_FORM_strx2:
+ set_cstr_value(v, resolve_strx(reader, read_uint16(&reader->p)));
+ break;
+ case DW_FORM_strx3:
+ set_cstr_value(v, resolve_strx(reader, read_uint24(&reader->p)));
+ break;
+ case DW_FORM_strx4:
+ set_cstr_value(v, resolve_strx(reader, read_uint32(&reader->p)));
+ break;
+ case DW_FORM_addrx1:
+ set_addr_idx_value(v, read_uint8(&reader->p));
+ break;
+ case DW_FORM_addrx2:
+ set_addr_idx_value(v, read_uint16(&reader->p));
+ break;
+ case DW_FORM_addrx3:
+ set_addr_idx_value(v, read_uint24(&reader->p));
+ break;
+ case DW_FORM_addrx4:
+ set_addr_idx_value(v, read_uint32(&reader->p));
+ break;
+ case 0:
+ goto fail;
+ break;
+ }
+ return;
+
+ fail:
+ fprintf(stderr, "%d: unsupported form: %#"PRIx64"\n", __LINE__, form);
+ exit(1);
+}
+
+/* find abbrev in current compilation unit */
+static const char *
+di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number)
+{
+ const char *p;
+ if (abbrev_number < ABBREV_TABLE_SIZE) {
+ return reader->abbrev_table[abbrev_number];
+ }
+ p = reader->abbrev_table[ABBREV_TABLE_SIZE-1];
+ /* skip 255th record */
+ uleb128(&p); /* tag */
+ p++; /* has_children */
+ di_skip_die_attributes(&p);
+ for (uint64_t n = uleb128(&p); abbrev_number != n; n = uleb128(&p)) {
+ if (n == 0) {
+ fprintf(stderr,"%d: Abbrev Number %"PRId64" not found\n",__LINE__, abbrev_number);
+ exit(1);
+ }
+ uleb128(&p); /* tag */
+ p++; /* has_children */
+ di_skip_die_attributes(&p);
+ }
+ return p;
+}
+
+#if 0
+static void
+hexdump0(const unsigned char *p, size_t n)
+{
+ size_t i;
+ fprintf(stderr, " 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
+ for (i=0; i < n; i++){
+ switch (i & 15) {
+ case 0:
+ fprintf(stderr, "%02" PRIdSIZE ": %02X ", i/16, p[i]);
+ break;
+ case 15:
+ fprintf(stderr, "%02X\n", p[i]);
+ break;
+ default:
+ fprintf(stderr, "%02X ", p[i]);
+ break;
+ }
+ }
+ if ((i & 15) != 15) {
+ fprintf(stderr, "\n");
+ }
+}
+#define hexdump(p,n) hexdump0((const unsigned char *)p, n)
+
+static void
+div_inspect(DebugInfoValue *v)
+{
+ switch (v->type) {
+ case VAL_uint:
+ fprintf(stderr,"%d: type:%d size:%" PRIxSIZE " v:%"PRIx64"\n",__LINE__,v->type,v->size,v->as.uint64);
+ break;
+ case VAL_int:
+ fprintf(stderr,"%d: type:%d size:%" PRIxSIZE " v:%"PRId64"\n",__LINE__,v->type,v->size,(int64_t)v->as.uint64);
+ break;
+ case VAL_cstr:
+ fprintf(stderr,"%d: type:%d size:%" PRIxSIZE " v:'%s'\n",__LINE__,v->type,v->size,v->as.ptr);
+ break;
+ case VAL_data:
+ fprintf(stderr,"%d: type:%d size:%" PRIxSIZE " v:\n",__LINE__,v->type,v->size);
+ hexdump(v->as.ptr, 16);
+ break;
+ }
+}
+#endif
+
+static DIE *
+di_read_die(DebugInfoReader *reader, DIE *die)
+{
+ uint64_t abbrev_number = uleb128(&reader->p);
+ if (abbrev_number == 0) {
+ reader->level--;
+ return NULL;
+ }
+
+ reader->q = di_find_abbrev(reader, abbrev_number);
+
+ die->pos = reader->p - reader->obj->debug_info.ptr - 1;
+ die->tag = (int)uleb128(&reader->q); /* tag */
+ die->has_children = *reader->q++; /* has_children */
+ if (die->has_children) {
+ reader->level++;
+ }
+ return die;
+}
+
+static DebugInfoValue *
+di_read_record(DebugInfoReader *reader, DebugInfoValue *vp)
+{
+ uint64_t at = uleb128(&reader->q);
+ uint64_t form = uleb128(&reader->q);
+ if (!at || !form) return NULL;
+ vp->at = at;
+ vp->form = form;
+ debug_info_reader_read_value(reader, form, vp);
+ return vp;
+}
+
+static void
+di_skip_records(DebugInfoReader *reader)
+{
+ for (;;) {
+ DebugInfoValue v = {{}};
+ uint64_t at = uleb128(&reader->q);
+ uint64_t form = uleb128(&reader->q);
+ if (!at || !form) return;
+ debug_info_reader_read_value(reader, form, &v);
+ }
+}
+
+typedef struct addr_header {
+ const char *ptr;
+ uint64_t unit_length;
+ uint8_t format;
+ uint8_t address_size;
+ /* uint8_t segment_selector_size; */
+} addr_header_t;
+
+static void
+addr_header_init(obj_info_t *obj, addr_header_t *header) {
+ const char *p = obj->debug_addr.ptr;
+
+ header->ptr = p;
+
+ if (!p) return;
+
+ header->unit_length = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+
+ header->format = 4;
+ if (header->unit_length == 0xffffffff) {
+ header->unit_length = *(uint64_t *)p;
+ p += sizeof(uint64_t);
+ header->format = 8;
+ }
+
+ p += 2; /* version */
+ header->address_size = *p++;
+ p++; /* segment_selector_size */
+}
+
+static uint64_t
+read_addr(addr_header_t *header, uint64_t addr_base, uint64_t idx) {
+ if (header->address_size == 4) {
+ return ((uint32_t*)(header->ptr + addr_base))[idx];
+ }
+ else {
+ return ((uint64_t*)(header->ptr + addr_base))[idx];
+ }
+}
+
+typedef struct rnglists_header {
+ uint64_t unit_length;
+ uint8_t format;
+ uint8_t address_size;
+ uint32_t offset_entry_count;
+} rnglists_header_t;
+
+static void
+rnglists_header_init(obj_info_t *obj, rnglists_header_t *header) {
+ const char *p = obj->debug_rnglists.ptr;
+
+ if (!p) return;
+
+ header->unit_length = *(uint32_t *)p;
+ p += sizeof(uint32_t);
+
+ header->format = 4;
+ if (header->unit_length == 0xffffffff) {
+ header->unit_length = *(uint64_t *)p;
+ p += sizeof(uint64_t);
+ header->format = 8;
+ }
+
+ p += 2; /* version */
+ header->address_size = *p++;
+ p++; /* segment_selector_size */
+ header->offset_entry_count = *(uint32_t *)p;
+}
+
+typedef struct {
+ uint64_t low_pc;
+ uint64_t high_pc;
+ uint64_t ranges;
+ bool low_pc_set;
+ bool high_pc_set;
+ bool ranges_set;
+} ranges_t;
+
+static void
+ranges_set(ranges_t *ptr, DebugInfoValue *v, addr_header_t *addr_header, uint64_t addr_base)
+{
+ uint64_t n = 0;
+ if (v->type == VAL_uint) {
+ n = v->as.uint64;
+ }
+ else if (v->type == VAL_addr) {
+ n = read_addr(addr_header, addr_base, v->as.addr_idx);
+ }
+ switch (v->at) {
+ case DW_AT_low_pc:
+ ptr->low_pc = n;
+ ptr->low_pc_set = true;
+ break;
+ case DW_AT_high_pc:
+ if (v->form == DW_FORM_addr) {
+ ptr->high_pc = n;
+ }
+ else {
+ ptr->high_pc = ptr->low_pc + n;
+ }
+ ptr->high_pc_set = true;
+ break;
+ case DW_AT_ranges:
+ ptr->ranges = n;
+ ptr->ranges_set = true;
+ break;
+ }
+}
+
+static uint64_t
+read_dw_form_addr(DebugInfoReader *reader, const char **ptr)
+{
+ const char *p = *ptr;
+ *ptr = p + reader->address_size;
+ if (reader->address_size == 4) {
+ return read_uint32(&p);
+ } else if (reader->address_size == 8) {
+ return read_uint64(&p);
+ } else {
+ fprintf(stderr,"unknown address_size:%d", reader->address_size);
+ abort();
+ }
+}
+
+static uintptr_t
+ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header)
+{
+ if (ptr->high_pc_set) {
+ if (ptr->ranges_set || !ptr->low_pc_set) {
+ exit(1);
+ }
+ if (ptr->low_pc <= addr && addr <= ptr->high_pc) {
+ return (uintptr_t)ptr->low_pc;
+ }
+ }
+ else if (ptr->ranges_set) {
+ /* TODO: support base address selection entry */
+ const char *p;
+ uint64_t base = ptr->low_pc_set ? ptr->low_pc : reader->current_low_pc;
+ bool base_valid = true;
+ if (reader->current_version >= 5) {
+ if (rnglists_header->offset_entry_count == 0) {
+ // DW_FORM_sec_offset
+ p = reader->obj->debug_rnglists.ptr + ptr->ranges + reader->current_rnglists_base;
+ }
+ else {
+ // DW_FORM_rnglistx
+ const char *offset_array = reader->obj->debug_rnglists.ptr + reader->current_rnglists_base;
+ if (rnglists_header->format == 4) {
+ p = offset_array + ((uint32_t *)offset_array)[ptr->ranges];
+ }
+ else {
+ p = offset_array + ((uint64_t *)offset_array)[ptr->ranges];
+ }
+ }
+ for (;;) {
+ uint8_t rle = read_uint8(&p);
+ uintptr_t from = 0, to = 0;
+ if (rle == DW_RLE_end_of_list) break;
+ switch (rle) {
+ case DW_RLE_base_addressx:
+ uleb128(&p);
+ base_valid = false; /* not supported yet */
+ break;
+ case DW_RLE_startx_endx:
+ uleb128(&p);
+ uleb128(&p);
+ break;
+ case DW_RLE_startx_length:
+ uleb128(&p);
+ uleb128(&p);
+ break;
+ case DW_RLE_offset_pair:
+ if (!base_valid) break;
+ from = (uintptr_t)base + uleb128(&p);
+ to = (uintptr_t)base + uleb128(&p);
+ break;
+ case DW_RLE_base_address:
+ base = read_dw_form_addr(reader, &p);
+ base_valid = true;
+ break;
+ case DW_RLE_start_end:
+ from = (uintptr_t)read_dw_form_addr(reader, &p);
+ to = (uintptr_t)read_dw_form_addr(reader, &p);
+ break;
+ case DW_RLE_start_length:
+ from = (uintptr_t)read_dw_form_addr(reader, &p);
+ to = from + uleb128(&p);
+ break;
+ }
+ if (from <= addr && addr < to) {
+ return from;
+ }
+ }
+ return false;
+ }
+ p = reader->obj->debug_ranges.ptr + ptr->ranges;
+ for (;;) {
+ uintptr_t from = read_uintptr(&p);
+ uintptr_t to = read_uintptr(&p);
+ if (!from && !to) break;
+ if (from == UINTPTR_MAX) {
+ /* base address selection entry */
+ base = to;
+ }
+ else if (base + from <= addr && addr < base + to) {
+ return (uintptr_t)base + from;
+ }
+ }
+ }
+ else if (ptr->low_pc_set) {
+ if (ptr->low_pc == addr) {
+ return (uintptr_t)ptr->low_pc;
+ }
+ }
+ return false;
+}
+
+#if 0
+static void
+ranges_inspect(DebugInfoReader *reader, ranges_t *ptr)
+{
+ if (ptr->high_pc_set) {
+ if (ptr->ranges_set || !ptr->low_pc_set) {
+ fprintf(stderr,"low_pc_set:%d high_pc_set:%d ranges_set:%d\n",ptr->low_pc_set,ptr->high_pc_set,ptr->ranges_set);
+ exit(1);
+ }
+ fprintf(stderr,"low_pc:%"PRIx64" high_pc:%"PRIx64"\n",ptr->low_pc,ptr->high_pc);
+ }
+ else if (ptr->ranges_set) {
+ char *p = reader->obj->debug_ranges.ptr + ptr->ranges;
+ fprintf(stderr,"low_pc:%"PRIx64" ranges:%"PRIx64" %lx ",ptr->low_pc,ptr->ranges, p-reader->obj->mapped);
+ for (;;) {
+ uintptr_t from = read_uintptr(&p);
+ uintptr_t to = read_uintptr(&p);
+ if (!from && !to) break;
+ fprintf(stderr,"%"PRIx64"-%"PRIx64" ",ptr->low_pc+from,ptr->low_pc+to);
+ }
+ fprintf(stderr,"\n");
+ }
+ else if (ptr->low_pc_set) {
+ fprintf(stderr,"low_pc:%"PRIx64"\n",ptr->low_pc);
+ }
+ else {
+ fprintf(stderr,"empty\n");
+ }
+}
+#endif
+
+static int
+di_read_cu(DebugInfoReader *reader)
+{
+ uint64_t unit_length;
+ uint16_t version;
+ uint64_t debug_abbrev_offset;
+ reader->format = 4;
+ reader->current_cu = reader->p;
+ unit_length = read_uint32(&reader->p);
+ if (unit_length == 0xffffffff) {
+ unit_length = read_uint64(&reader->p);
+ reader->format = 8;
+ }
+ reader->cu_end = reader->p + unit_length;
+ version = read_uint16(&reader->p);
+ reader->current_version = version;
+ if (version > 5) {
+ return -1;
+ }
+ else if (version == 5) {
+ /* unit_type = */ read_uint8(&reader->p);
+ reader->address_size = read_uint8(&reader->p);
+ debug_abbrev_offset = read_uint(reader);
+ }
+ else {
+ debug_abbrev_offset = read_uint(reader);
+ reader->address_size = read_uint8(&reader->p);
+ }
+ reader->q0 = reader->obj->debug_abbrev.ptr + debug_abbrev_offset;
+
+ reader->level = 0;
+ di_read_debug_abbrev_cu(reader);
+ if (di_read_debug_line_cu(reader)) return -1;
+
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER_BUILD_DATE)
+ /* Though DWARF specifies "the applicable base address defaults to the base
+ address of the compilation unit", but GCC seems to use zero as default */
+#else
+ do {
+ DIE die;
+
+ if (!di_read_die(reader, &die)) continue;
+
+ if (die.tag != DW_TAG_compile_unit) {
+ di_skip_records(reader);
+ break;
+ }
+
+ reader->current_str_offsets_base = 0;
+ reader->current_addr_base = 0;
+ reader->current_rnglists_base = 0;
+
+ DebugInfoValue low_pc = {{}};
+ /* enumerate abbrev */
+ for (;;) {
+ DebugInfoValue v = {{}};
+ if (!di_read_record(reader, &v)) break;
+ switch (v.at) {
+ case DW_AT_low_pc:
+ // clang may output DW_AT_addr_base after DW_AT_low_pc.
+ // We need to resolve the DW_FORM_addr* after DW_AT_addr_base is parsed.
+ low_pc = v;
+ break;
+ case DW_AT_str_offsets_base:
+ reader->current_str_offsets_base = v.as.uint64;
+ break;
+ case DW_AT_addr_base:
+ reader->current_addr_base = v.as.uint64;
+ break;
+ case DW_AT_rnglists_base:
+ reader->current_rnglists_base = v.as.uint64;
+ break;
+ }
+ }
+ // Resolve the DW_FORM_addr of DW_AT_low_pc
+ switch (low_pc.type) {
+ case VAL_uint:
+ reader->current_low_pc = low_pc.as.uint64;
+ break;
+ case VAL_addr:
+ {
+ addr_header_t header;
+ addr_header_init(reader->obj, &header);
+ reader->current_low_pc = read_addr(&header, reader->current_addr_base, low_pc.as.addr_idx);
+ }
+ break;
+ }
+ } while (0);
+#endif
+ return 0;
+}
+
+static void
+read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_origin, line_info_t *line)
+{
+ const char *p = reader->p;
+ const char *q = reader->q;
+ int level = reader->level;
+ DIE die;
+
+ switch (form) {
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ reader->p = reader->current_cu + abstract_origin;
+ break;
+ case DW_FORM_ref_addr:
+ goto finish; /* not supported yet */
+ case DW_FORM_ref_sig8:
+ goto finish; /* not supported yet */
+ case DW_FORM_ref_sup4:
+ case DW_FORM_ref_sup8:
+ goto finish; /* not supported yet */
+ default:
+ goto finish;
+ }
+ if (!di_read_die(reader, &die)) goto finish;
+
+ /* enumerate abbrev */
+ for (;;) {
+ DebugInfoValue v = {{}};
+ if (!di_read_record(reader, &v)) break;
+ switch (v.at) {
+ case DW_AT_name:
+ line->sname = get_cstr_value(&v);
+ break;
+ }
+ }
+
+ finish:
+ reader->p = p;
+ reader->q = q;
+ reader->level = level;
+}
+
+static void
+debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
+ line_info_t *lines, int offset) {
+
+ addr_header_t addr_header = {};
+ addr_header_init(reader->obj, &addr_header);
+
+ rnglists_header_t rnglists_header = {};
+ rnglists_header_init(reader->obj, &rnglists_header);
+
+ while (reader->p < reader->cu_end) {
+ DIE die;
+ ranges_t ranges = {};
+ line_info_t line = {};
+
+ if (!di_read_die(reader, &die)) continue;
+ /* fprintf(stderr,"%d:%tx: <%d>\n",__LINE__,die.pos,reader->level,die.tag); */
+
+ if (die.tag != DW_TAG_subprogram && die.tag != DW_TAG_inlined_subroutine) {
+ skip_die:
+ di_skip_records(reader);
+ continue;
+ }
+
+ /* enumerate abbrev */
+ for (;;) {
+ DebugInfoValue v = {{}};
+ /* ptrdiff_t pos = reader->p - reader->p0; */
+ if (!di_read_record(reader, &v)) break;
+ /* fprintf(stderr,"\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); */
+ /* div_inspect(&v); */
+ switch (v.at) {
+ case DW_AT_name:
+ line.sname = get_cstr_value(&v);
+ break;
+ case DW_AT_call_file:
+ fill_filename((int)v.as.uint64, reader->debug_line_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj);
+ break;
+ case DW_AT_call_line:
+ line.line = (int)v.as.uint64;
+ break;
+ case DW_AT_low_pc:
+ case DW_AT_high_pc:
+ case DW_AT_ranges:
+ ranges_set(&ranges, &v, &addr_header, reader->current_addr_base);
+ break;
+ case DW_AT_declaration:
+ goto skip_die;
+ case DW_AT_inline:
+ /* 1 or 3 */
+ break; /* goto skip_die; */
+ case DW_AT_abstract_origin:
+ read_abstract_origin(reader, v.form, v.as.uint64, &line);
+ break; /* goto skip_die; */
+ }
+ }
+ /* ranges_inspect(reader, &ranges); */
+ /* fprintf(stderr,"%d:%tx: %x ",__LINE__,diepos,die.tag); */
+ for (int i=offset; i < num_traces; i++) {
+ uintptr_t addr = (uintptr_t)traces[i];
+ uintptr_t offset = addr - reader->obj->base_addr + reader->obj->vmaddr;
+ uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header);
+ if (saddr) {
+ /* fprintf(stdout, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */
+ if (lines[i].sname) {
+ line_info_t *lp = malloc(sizeof(line_info_t));
+ memcpy(lp, &lines[i], sizeof(line_info_t));
+ lines[i].next = lp;
+ lp->dirname = line.dirname;
+ lp->filename = line.filename;
+ lp->line = line.line;
+ lp->saddr = 0;
+ }
+ lines[i].path = reader->obj->path;
+ lines[i].base_addr = line.base_addr;
+ lines[i].sname = line.sname;
+ lines[i].saddr = saddr + reader->obj->base_addr - reader->obj->vmaddr;
+ }
+ }
+ }
+}
+
+// This function parses the following attributes of Line Number Program Header in DWARF 5:
+//
+// * directory_entry_format_count
+// * directory_entry_format
+// * directories_count
+// * directories
+//
+// or
+//
+// * file_name_entry_format_count
+// * file_name_entry_format
+// * file_names_count
+// * file_names
+//
+// It records DW_LNCT_path and DW_LNCT_directory_index at the index "idx".
+static const char *
+parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, obj_info_t *obj, const char **out_path, uint64_t *out_directory_index) {
+ int i, j;
+ int entry_format_count = *(uint8_t *)p++;
+ const char *entry_format = p;
+
+ /* skip the part of entry_format */
+ for (i = 0; i < entry_format_count * 2; i++) uleb128(&p);
+
+ int entry_count = (int)uleb128(&p);
+
+ DebugInfoReader reader;
+ debug_info_reader_init(&reader, obj);
+ reader.format = format;
+ reader.p = p;
+ for (j = 0; j < entry_count; j++) {
+ const char *format = entry_format;
+ for (i = 0; i < entry_format_count; i++) {
+ DebugInfoValue v = {{}};
+ unsigned long dw_lnct = uleb128(&format);
+ unsigned long dw_form = uleb128(&format);
+ debug_info_reader_read_value(&reader, dw_form, &v);
+ if (dw_lnct == 1 /* DW_LNCT_path */ && v.type == VAL_cstr && out_path)
+ *out_path = v.as.ptr + v.off;
+ if (dw_lnct == 2 /* DW_LNCT_directory_index */ && v.type == VAL_uint && out_directory_index)
+ *out_directory_index = v.as.uint64;
+ }
+ if (j == idx) return 0;
+ }
+
+ return reader.p;
+}
+
+#ifdef USE_ELF
+static unsigned long
+uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr)
+{
+ *ptr = NULL;
+#ifdef SUPPORT_COMPRESSED_DEBUG_LINE
+ ElfW(Chdr) *chdr = (ElfW(Chdr) *)(file + shdr->sh_offset);
unsigned long destsize = chdr->ch_size;
int ret = 0;
if (chdr->ch_type != ELFCOMPRESS_ZLIB) {
/* unsupported compression type */
- return -1;
+ return 0;
}
- uncompressed_debug_line = malloc(destsize);
- if (!uncompressed_debug_line) return -1;
- ret = uncompress(uncompressed_debug_line, &destsize,
- (const Bytef *)debug_line + sizeof(ElfW(Chdr)), size-sizeof(ElfW(Chdr)));
+ *ptr = malloc(destsize);
+ if (!*ptr) return 0;
+ ret = uncompress((Bytef *)*ptr, &destsize,
+ (const Bytef*)chdr + sizeof(ElfW(Chdr)),
+ shdr->sh_size - sizeof(ElfW(Chdr)));
if (ret != Z_OK) goto fail;
- ret = parse_debug_line(num_traces, traces,
- uncompressed_debug_line,
- destsize,
- obj, lines, offset);
- if (ret) goto fail;
- obj->uncompressed_debug_line = uncompressed_debug_line;
- return 0;
+ return destsize;
fail:
- free(uncompressed_debug_line);
- return -1;
-}
+ free(*ptr);
+ *ptr = NULL;
#endif
+ return 0;
+}
/* read file and fill lines */
static uintptr_t
@@ -527,18 +2007,17 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
{
int i, j;
char *shstr;
- char *section_name;
ElfW(Ehdr) *ehdr;
ElfW(Shdr) *shdr, *shstr_shdr;
- ElfW(Shdr) *debug_line_shdr = NULL, *gnu_debuglink_shdr = NULL;
+ ElfW(Shdr) *gnu_debuglink_shdr = NULL;
+ ElfW(Shdr) *note_gnu_build_id = NULL;
int fd;
- off_t filesize = 0;
- char *file = NULL;
+ off_t filesize;
+ char *file;
ElfW(Shdr) *symtab_shdr = NULL, *strtab_shdr = NULL;
ElfW(Shdr) *dynsym_shdr = NULL, *dynstr_shdr = NULL;
obj_info_t *obj = *objp;
uintptr_t dladdr_fbase = 0;
- bool compressed_p = false;
fd = open(binary_filename, O_RDONLY);
if (fd < 0) {
@@ -586,7 +2065,7 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
shstr = file + shstr_shdr->sh_offset;
for (i = 0; i < ehdr->e_shnum; i++) {
- section_name = shstr + shdr[i].sh_name;
+ char *section_name = shstr + shdr[i].sh_name;
switch (shdr[i].sh_type) {
case SHT_STRTAB:
if (!strcmp(section_name, ".strtab")) {
@@ -604,16 +2083,44 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
/* if (!strcmp(section_name, ".dynsym")) */
dynsym_shdr = shdr + i;
break;
+ case SHT_NOTE:
+ if (!strcmp(section_name, ".note.gnu.build-id")) {
+ note_gnu_build_id = shdr + i;
+ }
+ break;
case SHT_PROGBITS:
- if (!strcmp(section_name, ".debug_line")) {
- if (shdr[i].sh_flags & SHF_COMPRESSED) {
- compressed_p = true;
- }
- debug_line_shdr = shdr + i;
- }
- else if (!strcmp(section_name, ".gnu_debuglink")) {
+ if (!strcmp(section_name, ".gnu_debuglink")) {
gnu_debuglink_shdr = shdr + i;
}
+ else {
+ const char *debug_section_names[] = {
+ ".debug_abbrev",
+ ".debug_info",
+ ".debug_line",
+ ".debug_ranges",
+ ".debug_str_offsets",
+ ".debug_addr",
+ ".debug_rnglists",
+ ".debug_str",
+ ".debug_line_str"
+ };
+
+ for (j=0; j < DWARF_SECTION_COUNT; j++) {
+ struct dwarf_section *s = obj_dwarf_section_at(obj, j);
+
+ if (strcmp(section_name, debug_section_names[j]) != 0)
+ continue;
+
+ s->ptr = file + shdr[i].sh_offset;
+ s->size = shdr[i].sh_size;
+ s->flags = shdr[i].sh_flags;
+ if (s->flags & SHF_COMPRESSED) {
+ s->size = uncompress_debug_section(&shdr[i], file, &s->ptr);
+ if (!s->size) goto fail;
+ }
+ break;
+ }
+ }
break;
}
}
@@ -625,21 +2132,22 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
char *strtab = file + dynstr_shdr->sh_offset;
ElfW(Sym) *symtab = (ElfW(Sym) *)(file + dynsym_shdr->sh_offset);
int symtab_count = (int)(dynsym_shdr->sh_size / sizeof(ElfW(Sym)));
- for (j = 0; j < symtab_count; j++) {
- ElfW(Sym) *sym = &symtab[j];
- Dl_info info;
- void *h, *s;
- if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size <= 0) continue;
- h = dlopen(NULL, RTLD_NOW|RTLD_LOCAL);
- if (!h) continue;
- s = dlsym(h, strtab + sym->st_name);
- if (s && dladdr(s, &info)) {
- dladdr_fbase = (uintptr_t)info.dli_fbase;
- dlclose(h);
- break;
- }
- dlclose(h);
- }
+ void *handle = dlopen(NULL, RTLD_NOW|RTLD_LOCAL);
+ if (handle) {
+ for (j = 0; j < symtab_count; j++) {
+ ElfW(Sym) *sym = &symtab[j];
+ Dl_info info;
+ void *s;
+ if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size == 0) continue;
+ s = dlsym(handle, strtab + sym->st_name);
+ if (s && dladdr(s, &info)) {
+ obj->base_addr = dladdr_fbase;
+ dladdr_fbase = (uintptr_t)info.dli_fbase;
+ break;
+ }
+ }
+ dlclose(handle);
+ }
if (ehdr->e_type == ET_EXEC) {
obj->base_addr = 0;
}
@@ -650,33 +2158,48 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
}
}
- if (!symtab_shdr) {
- symtab_shdr = dynsym_shdr;
- strtab_shdr = dynstr_shdr;
- }
-
- if (symtab_shdr && strtab_shdr) {
- char *strtab = file + strtab_shdr->sh_offset;
- ElfW(Sym) *symtab = (ElfW(Sym) *)(file + symtab_shdr->sh_offset);
- int symtab_count = (int)(symtab_shdr->sh_size / sizeof(ElfW(Sym)));
- for (j = 0; j < symtab_count; j++) {
- ElfW(Sym) *sym = &symtab[j];
- uintptr_t saddr = (uintptr_t)sym->st_value + obj->base_addr;
- if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size <= 0) continue;
- for (i = offset; i < num_traces; i++) {
- uintptr_t d = (uintptr_t)traces[i] - saddr;
- if (lines[i].line > 0 || d <= 0 || d > (uintptr_t)sym->st_size)
- continue;
- /* fill symbol name and addr from .symtab */
- lines[i].sname = strtab + sym->st_name;
- lines[i].saddr = saddr;
- lines[i].path = obj->path;
- lines[i].base_addr = obj->base_addr;
- }
- }
+ if (obj->debug_info.ptr && obj->debug_abbrev.ptr) {
+ DebugInfoReader reader;
+ debug_info_reader_init(&reader, obj);
+ i = 0;
+ while (reader.p < reader.pend) {
+ /* fprintf(stderr, "%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info.ptr, i++); */
+ if (di_read_cu(&reader)) goto use_symtab;
+ debug_info_read(&reader, num_traces, traces, lines, offset);
+ }
}
+ else {
+ /* This file doesn't have dwarf, use symtab or dynsym */
+use_symtab:
+ if (!symtab_shdr) {
+ /* This file doesn't have symtab, use dynsym instead */
+ symtab_shdr = dynsym_shdr;
+ strtab_shdr = dynstr_shdr;
+ }
- if (!debug_line_shdr) {
+ if (symtab_shdr && strtab_shdr) {
+ char *strtab = file + strtab_shdr->sh_offset;
+ ElfW(Sym) *symtab = (ElfW(Sym) *)(file + symtab_shdr->sh_offset);
+ int symtab_count = (int)(symtab_shdr->sh_size / sizeof(ElfW(Sym)));
+ for (j = 0; j < symtab_count; j++) {
+ ElfW(Sym) *sym = &symtab[j];
+ uintptr_t saddr = (uintptr_t)sym->st_value + obj->base_addr;
+ if (ELF_ST_TYPE(sym->st_info) != STT_FUNC) continue;
+ for (i = offset; i < num_traces; i++) {
+ uintptr_t d = (uintptr_t)traces[i] - saddr;
+ if (lines[i].line > 0 || d > (uintptr_t)sym->st_size)
+ continue;
+ /* fill symbol name and addr from .symtab */
+ if (!lines[i].sname) lines[i].sname = strtab + sym->st_name;
+ lines[i].saddr = saddr;
+ lines[i].path = obj->path;
+ lines[i].base_addr = obj->base_addr;
+ }
+ }
+ }
+ }
+
+ if (!obj->debug_line.ptr) {
/* This file doesn't have .debug_line section,
let's check .gnu_debuglink section instead. */
if (gnu_debuglink_shdr && check_debuglink) {
@@ -684,36 +2207,251 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
num_traces, traces,
objp, lines, offset);
}
+ if (note_gnu_build_id && check_debuglink) {
+ ElfW(Nhdr) *nhdr = (ElfW(Nhdr)*) (file + note_gnu_build_id->sh_offset);
+ const char *build_id = (char *)(nhdr + 1) + nhdr->n_namesz;
+ follow_debuglink_build_id(build_id, nhdr->n_descsz,
+ num_traces, traces,
+ objp, lines, offset);
+ }
goto finish;
}
- if (compressed_p) {
-#ifdef SUPPORT_COMPRESSED_DEBUG_LINE
- int r = parse_compressed_debug_line(num_traces, traces,
- file + debug_line_shdr->sh_offset,
- debug_line_shdr->sh_size,
- obj, lines, offset);
- if (r) goto fail;
+ if (parse_debug_line(num_traces, traces,
+ obj->debug_line.ptr,
+ obj->debug_line.size,
+ obj, lines, offset) == -1)
+ goto fail;
+
+finish:
+ return dladdr_fbase;
+fail:
+ return (uintptr_t)-1;
+}
+#else /* Mach-O */
+/* read file and fill lines */
+static uintptr_t
+fill_lines(int num_traces, void **traces, int check_debuglink,
+ obj_info_t **objp, line_info_t *lines, int offset)
+{
+# ifdef __LP64__
+# define LP(x) x##_64
+# else
+# define LP(x) x
+# endif
+ int fd;
+ off_t filesize;
+ char *file, *p = NULL;
+ obj_info_t *obj = *objp;
+ struct LP(mach_header) *header;
+ uintptr_t dladdr_fbase = 0;
+
+ {
+ char *s = binary_filename;
+ char *base = strrchr(binary_filename, '/')+1;
+ size_t max = PATH_MAX;
+ size_t size = strlen(binary_filename);
+ size_t basesize = size - (base - binary_filename);
+ s += size;
+ max -= size;
+ p = s;
+ size = strlcpy(s, ".dSYM/Contents/Resources/DWARF/", max);
+ if (size == 0) goto fail;
+ s += size;
+ max -= size;
+ if (max <= basesize) goto fail;
+ memcpy(s, base, basesize);
+ s[basesize] = 0;
+
+ fd = open(binary_filename, O_RDONLY);
+ if (fd < 0) {
+ *p = 0; /* binary_filename becomes original file name */
+ fd = open(binary_filename, O_RDONLY);
+ if (fd < 0) {
+ goto fail;
+ }
+ }
+ }
+
+ filesize = lseek(fd, 0, SEEK_END);
+ if (filesize < 0) {
+ int e = errno;
+ close(fd);
+ kprintf("lseek: %s\n", strerror(e));
+ goto fail;
+ }
+#if SIZEOF_OFF_T > SIZEOF_SIZE_T
+ if (filesize > (off_t)SIZE_MAX) {
+ close(fd);
+ kprintf("Too large file %s\n", binary_filename);
+ goto fail;
+ }
#endif
+ lseek(fd, 0, SEEK_SET);
+ /* async-signal unsafe */
+ file = (char *)mmap(NULL, (size_t)filesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (file == MAP_FAILED) {
+ int e = errno;
+ close(fd);
+ kprintf("mmap: %s\n", strerror(e));
+ goto fail;
+ }
+ close(fd);
+
+ obj->mapped = file;
+ obj->mapped_size = (size_t)filesize;
+
+ header = (struct LP(mach_header) *)file;
+ if (header->magic == LP(MH_MAGIC)) {
+ /* non universal binary */
+ p = file;
+ }
+ else if (header->magic == FAT_CIGAM) {
+ struct LP(mach_header) *mhp = _NSGetMachExecuteHeader();
+ struct fat_header *fat = (struct fat_header *)file;
+ char *q = file + sizeof(*fat);
+ uint32_t nfat_arch = __builtin_bswap32(fat->nfat_arch);
+ /* fprintf(stderr,"%d: fat:%s %d\n",__LINE__, binary_filename,nfat_arch); */
+ for (uint32_t i = 0; i < nfat_arch; i++) {
+ struct fat_arch *arch = (struct fat_arch *)q;
+ cpu_type_t cputype = __builtin_bswap32(arch->cputype);
+ cpu_subtype_t cpusubtype = __builtin_bswap32(arch->cpusubtype);
+ uint32_t offset = __builtin_bswap32(arch->offset);
+ /* fprintf(stderr,"%d: fat %d %x/%x %x/%x\n",__LINE__, i, mhp->cputype,mhp->cpusubtype, cputype,cpusubtype); */
+ if (mhp->cputype == cputype &&
+ (cpu_subtype_t)(mhp->cpusubtype & ~CPU_SUBTYPE_MASK) == cpusubtype) {
+ p = file + offset;
+ file = p;
+ header = (struct LP(mach_header) *)p;
+ if (header->magic == LP(MH_MAGIC)) {
+ goto found_mach_header;
+ }
+ break;
+ }
+ q += sizeof(*arch);
+ }
+ kprintf("'%s' is not a Mach-O universal binary file!\n",binary_filename);
+ close(fd);
+ goto fail;
}
else {
- int r = parse_debug_line(num_traces, traces,
- file + debug_line_shdr->sh_offset,
- debug_line_shdr->sh_size,
- obj, lines, offset);
- if (r) goto fail;
+ kprintf("'%s' is not a "
+# ifdef __LP64__
+ "64"
+# else
+ "32"
+# endif
+ "-bit Mach-O file!\n",binary_filename);
+ close(fd);
+ goto fail;
}
-finish:
+found_mach_header:
+ p += sizeof(*header);
+
+ for (uint32_t i = 0; i < (uint32_t)header->ncmds; i++) {
+ struct load_command *lcmd = (struct load_command *)p;
+ switch (lcmd->cmd) {
+ case LP(LC_SEGMENT):
+ {
+ static const char *debug_section_names[] = {
+ "__debug_abbrev",
+ "__debug_info",
+ "__debug_line",
+ "__debug_ranges",
+ "__debug_str_offsets",
+ "__debug_addr",
+ "__debug_rnglists",
+ "__debug_str",
+ "__debug_line_str",
+ };
+ struct LP(segment_command) *scmd = (struct LP(segment_command) *)lcmd;
+ if (strcmp(scmd->segname, "__TEXT") == 0) {
+ obj->vmaddr = scmd->vmaddr;
+ }
+ else if (strcmp(scmd->segname, "__DWARF") == 0) {
+ p += sizeof(struct LP(segment_command));
+ for (uint64_t i = 0; i < scmd->nsects; i++) {
+ struct LP(section) *sect = (struct LP(section) *)p;
+ p += sizeof(struct LP(section));
+ for (int j=0; j < DWARF_SECTION_COUNT; j++) {
+ struct dwarf_section *s = obj_dwarf_section_at(obj, j);
+
+ if (strcmp(sect->sectname, debug_section_names[j]) != 0)
+ continue;
+
+ s->ptr = file + sect->offset;
+ s->size = sect->size;
+ s->flags = sect->flags;
+ if (s->flags & SHF_COMPRESSED) {
+ goto fail;
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case LC_SYMTAB:
+ {
+ struct symtab_command *cmd = (struct symtab_command *)lcmd;
+ struct LP(nlist) *nl = (struct LP(nlist) *)(file + cmd->symoff);
+ char *strtab = file + cmd->stroff, *sname = 0;
+ uint32_t j;
+ uintptr_t saddr = 0;
+ /* kprintf("[%2d]: %x/symtab %p\n", i, cmd->cmd, (void *)p); */
+ for (j = 0; j < cmd->nsyms; j++) {
+ uintptr_t symsize, d;
+ struct LP(nlist) *e = &nl[j];
+ /* kprintf("[%2d][%4d]: %02x/%x/%x: %s %llx\n", i, j, e->n_type,e->n_sect,e->n_desc,strtab+e->n_un.n_strx,e->n_value); */
+ if (e->n_type != N_FUN) continue;
+ if (e->n_sect) {
+ saddr = (uintptr_t)e->n_value + obj->base_addr - obj->vmaddr;
+ sname = strtab + e->n_un.n_strx;
+ /* kprintf("[%2d][%4d]: %02x/%x/%x: %s %llx\n", i, j, e->n_type,e->n_sect,e->n_desc,strtab+e->n_un.n_strx,e->n_value); */
+ continue;
+ }
+ for (int k = offset; k < num_traces; k++) {
+ d = (uintptr_t)traces[k] - saddr;
+ symsize = e->n_value;
+ /* kprintf("%lx %lx %lx\n",saddr,symsize,traces[k]); */
+ if (lines[k].line > 0 || d > (uintptr_t)symsize)
+ continue;
+ /* fill symbol name and addr from .symtab */
+ if (!lines[k].sname) lines[k].sname = sname;
+ lines[k].saddr = saddr;
+ lines[k].path = obj->path;
+ lines[k].base_addr = obj->base_addr;
+ }
+ }
+ }
+ }
+ p += lcmd->cmdsize;
+ }
+
+ if (obj->debug_info.ptr && obj->debug_abbrev.ptr) {
+ DebugInfoReader reader;
+ debug_info_reader_init(&reader, obj);
+ while (reader.p < reader.pend) {
+ if (di_read_cu(&reader)) goto fail;
+ debug_info_read(&reader, num_traces, traces, lines, offset);
+ }
+ }
+
+ if (parse_debug_line(num_traces, traces,
+ obj->debug_line.ptr,
+ obj->debug_line.size,
+ obj, lines, offset) == -1)
+ goto fail;
+
return dladdr_fbase;
fail:
- if (file != NULL) {
- munmap(file, (size_t)filesize);
- }
return (uintptr_t)-1;
}
+#endif
#define HAVE_MAIN_EXE_PATH
-#if defined(__FreeBSD__)
+#if defined(__FreeBSD__) || defined(__DragonFly__)
# include <sys/sysctl.h>
#endif
/* ssize_t main_exe_path(void)
@@ -722,16 +2460,21 @@ fail:
* and returns strlen(binary_filename).
* it is NUL terminated.
*/
-#if defined(__linux__)
+#if defined(__linux__) || defined(__NetBSD__)
static ssize_t
main_exe_path(void)
{
-# define PROC_SELF_EXE "/proc/self/exe"
+# if defined(__linux__)
+# define PROC_SELF_EXE "/proc/self/exe"
+# elif defined(__NetBSD__)
+# define PROC_SELF_EXE "/proc/curproc/exe"
+# endif
ssize_t len = readlink(PROC_SELF_EXE, binary_filename, PATH_MAX);
+ if (len < 0) return 0;
binary_filename[len] = 0;
return len;
}
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
static ssize_t
main_exe_path(void)
{
@@ -745,10 +2488,69 @@ main_exe_path(void)
len--; /* sysctl sets strlen+1 */
return len;
}
+#elif defined(HAVE_LIBPROC_H)
+static ssize_t
+main_exe_path(void)
+{
+ int len = proc_pidpath(getpid(), binary_filename, PATH_MAX);
+ if (len == 0) return 0;
+ binary_filename[len] = 0;
+ return len;
+}
#else
#undef HAVE_MAIN_EXE_PATH
#endif
+static void
+print_line0(line_info_t *line, void *address)
+{
+ uintptr_t addr = (uintptr_t)address;
+ uintptr_t d = addr - line->saddr;
+ if (!address) {
+ /* inlined */
+ if (line->dirname && line->dirname[0]) {
+ kprintf("%s(%s) %s/%s:%d\n", line->path, line->sname, line->dirname, line->filename, line->line);
+ }
+ else {
+ kprintf("%s(%s) %s:%d\n", line->path, line->sname, line->filename, line->line);
+ }
+ }
+ else if (!line->path) {
+ kprintf("[0x%"PRIxPTR"]\n", addr);
+ }
+ else if (!line->sname) {
+ kprintf("%s(0x%"PRIxPTR") [0x%"PRIxPTR"]\n", line->path, addr-line->base_addr, addr);
+ }
+ else if (!line->saddr) {
+ kprintf("%s(%s) [0x%"PRIxPTR"]\n", line->path, line->sname, addr);
+ }
+ else if (line->line <= 0) {
+ kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"]\n", line->path, line->sname,
+ d, addr);
+ }
+ else if (!line->filename) {
+ kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"] ???:%d\n", line->path, line->sname,
+ d, addr, line->line);
+ }
+ else if (line->dirname && line->dirname[0]) {
+ kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"] %s/%s:%d\n", line->path, line->sname,
+ d, addr, line->dirname, line->filename, line->line);
+ }
+ else {
+ kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"] %s:%d\n", line->path, line->sname,
+ d, addr, line->filename, line->line);
+ }
+}
+
+static void
+print_line(line_info_t *line, void *address)
+{
+ print_line0(line, address);
+ if (line->next) {
+ print_line(line->next, NULL);
+ }
+}
+
void
rb_dump_backtrace_with_lines(int num_traces, void **traces)
{
@@ -758,6 +2560,7 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
obj_info_t *obj = NULL;
/* 2 is NULL + main executable */
void **dladdr_fbases = (void **)calloc(num_traces+2, sizeof(void *));
+
#ifdef HAVE_MAIN_EXE_PATH
char *main_path = NULL; /* used on printing backtrace */
ssize_t len;
@@ -788,8 +2591,8 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
/* if the binary is strip-ed, this may effect */
for (p=dladdr_fbases; *p; p++) {
if (*p == info.dli_fbase) {
- lines[i].path = info.dli_fname;
- lines[i].sname = info.dli_sname;
+ if (info.dli_fname) lines[i].path = info.dli_fname;
+ if (info.dli_sname) lines[i].sname = info.dli_sname;
goto next_line;
}
}
@@ -799,7 +2602,11 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces)
obj->base_addr = (uintptr_t)info.dli_fbase;
path = info.dli_fname;
obj->path = path;
- lines[i].path = path;
+ if (path) lines[i].path = path;
+ if (info.dli_sname) {
+ lines[i].sname = info.dli_sname;
+ lines[i].saddr = (uintptr_t)info.dli_saddr;
+ }
strlcpy(binary_filename, path, PATH_MAX);
if (fill_lines(num_traces, traces, 1, &obj, lines, i) == (uintptr_t)-1)
break;
@@ -810,48 +2617,36 @@ next_line:
/* output */
for (i = 0; i < num_traces; i++) {
- line_info_t *line = &lines[i];
- uintptr_t addr = (uintptr_t)traces[i];
- uintptr_t d = addr - line->saddr;
- if (!line->path) {
- kprintf("[0x%lx]\n", addr);
- }
- else if (!line->saddr || !line->sname) {
- kprintf("%s(0x%lx) [0x%lx]\n", line->path, addr-line->base_addr, addr);
- }
- else if (line->line <= 0) {
- kprintf("%s(%s+0x%lx) [0x%lx]\n", line->path, line->sname,
- d, addr);
- }
- else if (!line->filename) {
- kprintf("%s(%s+0x%lx) [0x%lx] ???:%d\n", line->path, line->sname,
- d, addr, line->line);
- }
- else if (line->dirname && line->dirname[0]) {
- kprintf("%s(%s+0x%lx) [0x%lx] %s/%s:%d\n", line->path, line->sname,
- d, addr, line->dirname, line->filename, line->line);
- }
- else {
- kprintf("%s(%s+0x%lx) [0x%lx] %s:%d\n", line->path, line->sname,
- d, addr, line->filename, line->line);
- }
+ print_line(&lines[i], traces[i]);
+
/* FreeBSD's backtrace may show _start and so on */
- if (line->sname && strcmp("main", line->sname) == 0)
+ if (lines[i].sname && strcmp("main", lines[i].sname) == 0)
break;
}
/* free */
while (obj) {
obj_info_t *o = obj;
- obj = o->next;
- if (o->mapped_size) {
- munmap(o->mapped, o->mapped_size);
- }
- if (o->uncompressed_debug_line) {
- free(o->uncompressed_debug_line);
+ for (i=0; i < DWARF_SECTION_COUNT; i++) {
+ struct dwarf_section *s = obj_dwarf_section_at(obj, i);
+ if (s->flags & SHF_COMPRESSED) {
+ free(s->ptr);
+ }
}
+ if (obj->mapped_size) {
+ munmap(obj->mapped, obj->mapped_size);
+ }
+ obj = o->next;
free(o);
}
+ for (i = 0; i < num_traces; i++) {
+ line_info_t *line = lines[i].next;
+ while (line) {
+ line_info_t *l = line;
+ line = line->next;
+ free(l);
+ }
+ }
free(lines);
free(dladdr_fbases);
}
@@ -897,7 +2692,7 @@ next_line:
#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
static inline int toupper(int c) { return ('A' <= c && c <= 'Z') ? (c&0x5f) : c; }
#define hex2ascii(hex) (hex2ascii_data[hex])
-char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+static const char hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static inline int imax(int a, int b) { return (a > b ? a : b); }
static int kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap);
diff --git a/addr2line.h b/addr2line.h
index d99f010934..f09b665800 100644
--- a/addr2line.h
+++ b/addr2line.h
@@ -1,3 +1,5 @@
+#ifndef RUBY_ADDR2LINE_H
+#define RUBY_ADDR2LINE_H
/**********************************************************************
addr2line.h -
@@ -8,10 +10,7 @@
**********************************************************************/
-#ifndef RUBY_ADDR2LINE_H
-#define RUBY_ADDR2LINE_H
-
-#ifdef USE_ELF
+#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H))
void
rb_dump_backtrace_with_lines(int num_traces, void **traces);
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index bb48531502..0000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,137 +0,0 @@
----
-version: '{build}'
-shallow_clone: true
-platform:
- - x64
-environment:
- ruby_version: "24-%Platform%"
- zlib_version: "1.2.11"
- matrix:
- - build: vs
- vs: 120
- - build: vs
- vs: 140
- - build: msys2
-matrix:
- fast_finish: true
-notifications:
- - provider: Webhook
- url:
- secure: iMINHMS0nZabaDsxN9omRDsekxzVvAAzEq5ev7lN6vZ6r9zNhl3/F/7POIVyahAwVFpRDeQT/iUugpAGWmOt3eGL/lZIdqiJFZ9DjPSkP68= # #alerts
- method: POST
- # "icon_url" is the url used by `provider: Slack`
- body: >-
- {{^isPullRequest}}
- {
- "attachments": [
- {
- "title": "Build {{projectName}} {{buildVersion}} {{status}}",
- "fallback": "AppVeyor Build {{projectName}} {{buildVersion}} {{status}}",
- "title_link": "{{buildUrl}}",
- "text": "Commit <{{commitUrl}}|{{commitId}}> by {{commitAuthor}} on {{commitDate}}: _{{commitMessage}}_",
- {{#passed}}
- "color": "#44ee44"
- {{/passed}}
- {{#failed}}
- "color": "#ee4444"
- {{/failed}}
- }
- ],
- "icon_url": "https://slack-files2.s3-us-west-2.amazonaws.com/bot_icons/2018-02-10/314363543719_48.png",
- "username": "AppVeyor CI"
- }
- {{/isPullRequest}}
- on_build_success: false
- on_build_failure: true
- on_build_status_changed: true
-for:
--
- matrix:
- only:
- - build: vs
- install:
- - ver
- - chcp
- - SET BITS=%Platform:x86=32%
- - SET BITS=%BITS:x=%
- - SET OPENSSL_DIR=c:\OpenSSL-Win%BITS%
- - CALL SET vcvars=%%^VS%VS%COMNTOOLS^%%..\..\VC\vcvarsall.bat
- - SET vcvars
- - '"%vcvars%" %Platform:x64=amd64%'
- - SET ruby_path=C:\Ruby%ruby_version:-x86=%
- - SET PATH=\usr\local\bin;%ruby_path%\bin;%PATH%;C:\msys64\mingw64\bin;C:\msys64\usr\bin
- - ruby --version
- - 'cl'
- - SET
- - echo> Makefile srcdir=.
- - echo>> Makefile MSC_VER=0
- - echo>> Makefile RT=none
- - echo>> Makefile RT_VER=0
- - echo>> Makefile BUILTIN_ENCOBJS=nul
- - type win32\Makefile.sub >> Makefile
- - nmake %mflags% touch-unicode-files
- - nmake %mflags% up incs UNICODE_FILES=.
- - del Makefile
- - mkdir \usr\local\bin
- - mkdir \usr\local\include
- - mkdir \usr\local\lib
- - appveyor DownloadFile https://zlib.net/zlib%zlib_version:.=%.zip
- - 7z x -o%APPVEYOR_BUILD_FOLDER%\ext\zlib zlib%zlib_version:.=%.zip
- - for %%I in (%OPENSSL_DIR%\*.dll) do mklink /h \usr\local\bin\%%~nxI %%I
- - mkdir %Platform%-mswin_%vs%
- - ps: Get-ChildItem "win32" -Recurse | foreach {$_.Attributes = 'Readonly'}
- - ps: Get-Item $env:Platform"-mswin_"$env:vs | foreach {$_.Attributes = 'Normal'}
- build_script:
- - cd %APPVEYOR_BUILD_FOLDER%
- - cd %Platform%-mswin_%vs%
- - ..\win32\configure.bat --without-ext=+,dbm,gdbm,readline --with-opt-dir=/usr/local --with-openssl-dir=%OPENSSL_DIR:\=/%
- - nmake -l
- - nmake install-nodoc
- - \usr\bin\ruby -v -e "p :locale => Encoding.find('locale'), :filesystem => Encoding.find('filesystem')"
- test_script:
- - set /a JOBS=%NUMBER_OF_PROCESSORS%
- - nmake -l "TESTOPTS=-v -q" btest
- - nmake -l "TESTOPTS=-v -q" test-basic
- - nmake -l "TESTOPTS=-q -j%JOBS% --subprocess-timeout-scale=1.5" test-all RUBY_FORCE_TEST_JIT=1
- - nmake -l test-spec
--
- matrix:
- only:
- - build: msys2
- install:
- - ver
- - chcp
- - set /a JOBS=%NUMBER_OF_PROCESSORS%
- - set MSYS_NO_PATHCONV=1
- - SET MSYSTEM=%Platform:x86=32%
- - SET MSYSTEM=%MSYSTEM:x=MINGW%
- - SET MSYS2_ARCH=%Platform:x86=i686%
- - SET MSYS2_ARCH=%MSYS2_ARCH:x64=x86_64%
- - set MSYSTEM_PREFIX=/mingw64
- - set MINGW_CHOST=%MSYS2_ARCH%-w64-mingw32
- - SET ruby_path=C:\Ruby%ruby_version:-x86=%
- - cd ..
- - mkdir build
- - mkdir install
- - SET PATH=%ruby_path%\bin;C:\msys64\%MSYSTEM%\bin;C:\msys64\usr\bin;%PATH%
- - ruby --version
- build_script:
- # always update database
- - pacman -Sy
- - pacman -S --noconfirm --needed --noprogressbar mingw-w64-x86_64-toolchain
- - pacman -S --noconfirm --needed --noprogressbar mingw-w64-x86_64-gdbm mingw-w64-x86_64-gmp mingw-w64-x86_64-libffi mingw-w64-x86_64-ncurses mingw-w64-x86_64-readline mingw-w64-x86_64-zlib
- - cd %APPVEYOR_BUILD_FOLDER%
- - set CFLAGS=-march=%MSYS2_ARCH:_=-% -mtune=generic -O3 -pipe
- - set CXXFLAGS=%CFLAGS%
- - set CPPFLAGS=-D_FORTIFY_SOURCE=2 -D__USE_MINGW_ANSI_STDIO=1 -DFD_SETSIZE=2048
- - set LDFLAGS=-pipe
- - sh -c "autoreconf -fi"
- - cd ..\build
- - sh ../ruby/configure --disable-install-doc --prefix=/. --build=%MINGW_CHOST% --host=%MINGW_CHOST% --target=%MINGW_CHOST%
- - mingw32-make -j%JOBS% up
- - mingw32-make -j%JOBS%
- - mingw32-make DESTDIR=../install install-nodoc
- test_script:
- - mingw32-make test
- - mingw32-make test-all TESTOPTS="-j %JOBS% --retry --job-status=normal --show-skip --subprocess-timeout-scale=1.5" RUBY_FORCE_TEST_JIT=1 V=1
- - mingw32-make test-spec MSPECOPT=-j
diff --git a/array.c b/array.c
index 3c36bb66e6..b76e9a64a3 100644
--- a/array.c
+++ b/array.c
@@ -11,21 +11,65 @@
**********************************************************************/
-#include "ruby/encoding.h"
-#include "ruby/util.h"
-#include "ruby/st.h"
+#include "debug_counter.h"
+#include "id.h"
#include "internal.h"
+#include "internal/array.h"
+#include "internal/compar.h"
+#include "internal/enum.h"
+#include "internal/gc.h"
+#include "internal/hash.h"
+#include "internal/numeric.h"
+#include "internal/object.h"
+#include "internal/proc.h"
+#include "internal/rational.h"
+#include "internal/vm.h"
#include "probes.h"
-#include "id.h"
-#include "debug_counter.h"
+#include "ruby/encoding.h"
+#include "ruby/st.h"
+#include "ruby/util.h"
+#include "transient_heap.h"
+#include "builtin.h"
-#ifndef ARRAY_DEBUG
+#if !ARRAY_DEBUG
+# undef NDEBUG
# define NDEBUG
#endif
#include "ruby_assert.h"
VALUE rb_cArray;
+/* Flags of RArray
+ *
+ * 1: RARRAY_EMBED_FLAG
+ * The array is embedded (its contents follow the header, rather than
+ * being on a separately allocated buffer).
+ * 2: RARRAY_SHARED_FLAG (equal to ELTS_SHARED)
+ * The array is shared. The buffer this array points to is owned by
+ * another array (the shared root).
+ * if USE_RVARGC
+ * 3-9: RARRAY_EMBED_LEN
+ * The length of the array when RARRAY_EMBED_FLAG is set.
+ * else
+ * 3-4: RARRAY_EMBED_LEN
+ * The length of the array when RARRAY_EMBED_FLAG is set.
+ * endif
+ * 12: RARRAY_SHARED_ROOT_FLAG
+ * The array is a shared root that does reference counting. The buffer
+ * this array points to is owned by this array but may be pointed to
+ * by other arrays.
+ * Note: Frozen arrays may be a shared root without this flag being
+ * set. Frozen arrays do not have reference counting because
+ * they cannot be modified. Not updating the reference count
+ * improves copy-on-write performance. Their reference count is
+ * assumed to be infinity.
+ * 13: RARRAY_TRANSIENT_FLAG
+ * The buffer of the array is allocated on the transient heap.
+ * 14: RARRAY_PTR_IN_USE_FLAG
+ * The buffer of the array is in use. This is only used during
+ * debugging.
+ */
+
/* for OPTIMIZED_CMP: */
#define id_cmp idCmp
@@ -33,33 +77,41 @@ VALUE rb_cArray;
#define ARY_MAX_SIZE (LONG_MAX / (int)sizeof(VALUE))
#define SMALL_ARRAY_LEN 16
-# define ARY_SHARED_P(ary) \
- (assert(!FL_TEST((ary), ELTS_SHARED) || !FL_TEST((ary), RARRAY_EMBED_FLAG)), \
- FL_TEST((ary),ELTS_SHARED)!=0)
-# define ARY_EMBED_P(ary) \
- (assert(!FL_TEST((ary), ELTS_SHARED) || !FL_TEST((ary), RARRAY_EMBED_FLAG)), \
- FL_TEST((ary), RARRAY_EMBED_FLAG)!=0)
+RBIMPL_ATTR_MAYBE_UNUSED()
+static int
+should_be_T_ARRAY(VALUE ary)
+{
+ return RB_TYPE_P(ary, T_ARRAY);
+}
#define ARY_HEAP_PTR(a) (assert(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.ptr)
#define ARY_HEAP_LEN(a) (assert(!ARY_EMBED_P(a)), RARRAY(a)->as.heap.len)
+#define ARY_HEAP_CAPA(a) (assert(!ARY_EMBED_P(a)), assert(!ARY_SHARED_ROOT_P(a)), \
+ RARRAY(a)->as.heap.aux.capa)
+
#define ARY_EMBED_PTR(a) (assert(ARY_EMBED_P(a)), RARRAY(a)->as.ary)
#define ARY_EMBED_LEN(a) \
(assert(ARY_EMBED_P(a)), \
(long)((RBASIC(a)->flags >> RARRAY_EMBED_LEN_SHIFT) & \
- (RARRAY_EMBED_LEN_MASK >> RARRAY_EMBED_LEN_SHIFT)))
-#define ARY_HEAP_SIZE(a) (assert(!ARY_EMBED_P(a)), assert(ARY_OWNS_HEAP_P(a)), RARRAY(a)->as.heap.aux.capa * sizeof(VALUE))
+ (RARRAY_EMBED_LEN_MASK >> RARRAY_EMBED_LEN_SHIFT)))
+#define ARY_HEAP_SIZE(a) (assert(!ARY_EMBED_P(a)), assert(ARY_OWNS_HEAP_P(a)), ARY_CAPA(a) * sizeof(VALUE))
+
+#define ARY_OWNS_HEAP_P(a) (assert(should_be_T_ARRAY((VALUE)(a))), \
+ !FL_TEST_RAW((a), RARRAY_SHARED_FLAG|RARRAY_EMBED_FLAG))
-#define ARY_OWNS_HEAP_P(a) (!FL_TEST((a), ELTS_SHARED|RARRAY_EMBED_FLAG))
#define FL_SET_EMBED(a) do { \
assert(!ARY_SHARED_P(a)); \
FL_SET((a), RARRAY_EMBED_FLAG); \
+ RARY_TRANSIENT_UNSET(a); \
+ ary_verify(a); \
} while (0)
+
#define FL_UNSET_EMBED(ary) FL_UNSET((ary), RARRAY_EMBED_FLAG|RARRAY_EMBED_LEN_MASK)
#define FL_SET_SHARED(ary) do { \
assert(!ARY_EMBED_P(ary)); \
- FL_SET((ary), ELTS_SHARED); \
+ FL_SET((ary), RARRAY_SHARED_FLAG); \
} while (0)
-#define FL_UNSET_SHARED(ary) FL_UNSET((ary), ELTS_SHARED)
+#define FL_UNSET_SHARED(ary) FL_UNSET((ary), RARRAY_SHARED_FLAG)
#define ARY_SET_PTR(ary, p) do { \
assert(!ARY_EMBED_P(ary)); \
@@ -69,7 +121,6 @@ VALUE rb_cArray;
#define ARY_SET_EMBED_LEN(ary, n) do { \
long tmp_n = (n); \
assert(ARY_EMBED_P(ary)); \
- assert(!OBJ_FROZEN(ary)); \
RBASIC(ary)->flags &= ~RARRAY_EMBED_LEN_MASK; \
RBASIC(ary)->flags |= (tmp_n) << RARRAY_EMBED_LEN_SHIFT; \
} while (0)
@@ -101,8 +152,8 @@ VALUE rb_cArray;
} \
} while (0)
-#define ARY_CAPA(ary) (ARY_EMBED_P(ary) ? RARRAY_EMBED_LEN_MAX : \
- ARY_SHARED_ROOT_P(ary) ? RARRAY_LEN(ary) : RARRAY(ary)->as.heap.aux.capa)
+#define ARY_CAPA(ary) (ARY_EMBED_P(ary) ? ary_embed_capa(ary) : \
+ ARY_SHARED_ROOT_P(ary) ? RARRAY_LEN(ary) : ARY_HEAP_CAPA(ary))
#define ARY_SET_CAPA(ary, n) do { \
assert(!ARY_EMBED_P(ary)); \
assert(!ARY_SHARED_P(ary)); \
@@ -110,44 +161,185 @@ VALUE rb_cArray;
RARRAY(ary)->as.heap.aux.capa = (n); \
} while (0)
-#define ARY_SHARED(ary) (assert(ARY_SHARED_P(ary)), RARRAY(ary)->as.heap.aux.shared)
#define ARY_SET_SHARED(ary, value) do { \
const VALUE _ary_ = (ary); \
const VALUE _value_ = (value); \
assert(!ARY_EMBED_P(_ary_)); \
assert(ARY_SHARED_P(_ary_)); \
- assert(ARY_SHARED_ROOT_P(_value_)); \
- RB_OBJ_WRITE(_ary_, &RARRAY(_ary_)->as.heap.aux.shared, _value_); \
+ assert(!OBJ_FROZEN(_ary_)); \
+ assert(ARY_SHARED_ROOT_P(_value_) || OBJ_FROZEN(_value_)); \
+ RB_OBJ_WRITE(_ary_, &RARRAY(_ary_)->as.heap.aux.shared_root, _value_); \
} while (0)
-#define RARRAY_SHARED_ROOT_FLAG FL_USER5
-#define ARY_SHARED_ROOT_P(ary) (FL_TEST((ary), RARRAY_SHARED_ROOT_FLAG))
-#define ARY_SHARED_NUM(ary) \
- (assert(ARY_SHARED_ROOT_P(ary)), RARRAY(ary)->as.heap.aux.capa)
-#define ARY_SHARED_OCCUPIED(ary) (ARY_SHARED_NUM(ary) == 1)
-#define ARY_SET_SHARED_NUM(ary, value) do { \
+
+#define ARY_SHARED_ROOT_OCCUPIED(ary) (!OBJ_FROZEN(ary) && ARY_SHARED_ROOT_REFCNT(ary) == 1)
+#define ARY_SET_SHARED_ROOT_REFCNT(ary, value) do { \
assert(ARY_SHARED_ROOT_P(ary)); \
+ assert(!OBJ_FROZEN(ary)); \
+ assert((value) >= 0); \
RARRAY(ary)->as.heap.aux.capa = (value); \
} while (0)
#define FL_SET_SHARED_ROOT(ary) do { \
+ assert(!OBJ_FROZEN(ary)); \
assert(!ARY_EMBED_P(ary)); \
+ assert(!RARRAY_TRANSIENT_P(ary)); \
FL_SET((ary), RARRAY_SHARED_ROOT_FLAG); \
} while (0)
-#define ARY_SET(a, i, v) RARRAY_ASET((assert(!ARY_SHARED_P(a)), (a)), (i), (v))
+static inline void
+ARY_SET(VALUE a, long i, VALUE v)
+{
+ assert(!ARY_SHARED_P(a));
+ assert(!OBJ_FROZEN(a));
+
+ RARRAY_ASET(a, i, v);
+}
+#undef RARRAY_ASET
+
+static long
+ary_embed_capa(VALUE ary)
+{
+#if USE_RVARGC
+ size_t size = rb_gc_obj_slot_size(ary) - offsetof(struct RArray, as.ary);
+ assert(size % sizeof(VALUE) == 0);
+ return size / sizeof(VALUE);
+#else
+ return RARRAY_EMBED_LEN_MAX;
+#endif
+}
+
+static size_t
+ary_embed_size(long capa)
+{
+ return offsetof(struct RArray, as.ary) + (sizeof(VALUE) * capa);
+}
+
+static bool
+ary_embeddable_p(long capa)
+{
+#if USE_RVARGC
+ return rb_gc_size_allocatable_p(ary_embed_size(capa));
+#else
+ return capa <= RARRAY_EMBED_LEN_MAX;
+#endif
+}
+
+bool
+rb_ary_embeddable_p(VALUE ary)
+{
+ /* An array cannot be turned embeddable when the array is:
+ * - Shared root: other objects may point to the buffer of this array
+ * so we cannot make it embedded.
+ * - Frozen: this array may also be a shared root without the shared root
+ * flag.
+ * - Shared: we don't want to re-embed an array that points to a shared
+ * root (to save memory).
+ */
+ return !(ARY_SHARED_ROOT_P(ary) || OBJ_FROZEN(ary) || ARY_SHARED_P(ary));
+}
+
+size_t
+rb_ary_size_as_embedded(VALUE ary)
+{
+ size_t real_size;
+
+ if (ARY_EMBED_P(ary)) {
+ real_size = ary_embed_size(ARY_EMBED_LEN(ary));
+ }
+ else if (rb_ary_embeddable_p(ary)) {
+ real_size = ary_embed_size(ARY_HEAP_CAPA(ary));
+ }
+ else {
+ real_size = sizeof(struct RArray);
+ }
+ return real_size;
+}
+
+
+#if ARRAY_DEBUG
+#define ary_verify(ary) ary_verify_(ary, __FILE__, __LINE__)
+
+static VALUE
+ary_verify_(VALUE ary, const char *file, int line)
+{
+ assert(RB_TYPE_P(ary, T_ARRAY));
+
+ if (ARY_SHARED_P(ary)) {
+ VALUE root = ARY_SHARED_ROOT(ary);
+ const VALUE *ptr = ARY_HEAP_PTR(ary);
+ const VALUE *root_ptr = RARRAY_CONST_PTR_TRANSIENT(root);
+ long len = ARY_HEAP_LEN(ary), root_len = RARRAY_LEN(root);
+ assert(ARY_SHARED_ROOT_P(root) || OBJ_FROZEN(root));
+ assert(root_ptr <= ptr && ptr + len <= root_ptr + root_len);
+ ary_verify(root);
+ }
+ else if (ARY_EMBED_P(ary)) {
+ assert(!RARRAY_TRANSIENT_P(ary));
+ assert(!ARY_SHARED_P(ary));
+ assert(RARRAY_LEN(ary) <= ary_embed_capa(ary));
+ }
+ else {
+#if 1
+ const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ long i, len = RARRAY_LEN(ary);
+ volatile VALUE v;
+ if (len > 1) len = 1; /* check only HEAD */
+ for (i=0; i<len; i++) {
+ v = ptr[i]; /* access check */
+ }
+ v = v;
+#endif
+ }
+
+#if USE_TRANSIENT_HEAP
+ if (RARRAY_TRANSIENT_P(ary)) {
+ assert(rb_transient_heap_managed_ptr_p(RARRAY_CONST_PTR_TRANSIENT(ary)));
+ }
+#endif
+
+ rb_transient_heap_verify();
+
+ return ary;
+}
+
+void
+rb_ary_verify(VALUE ary)
+{
+ ary_verify(ary);
+}
+#else
+#define ary_verify(ary) ((void)0)
+#endif
+
+VALUE *
+rb_ary_ptr_use_start(VALUE ary)
+{
+#if ARRAY_DEBUG
+ FL_SET_RAW(ary, RARRAY_PTR_IN_USE_FLAG);
+#endif
+ return (VALUE *)RARRAY_CONST_PTR_TRANSIENT(ary);
+}
void
-rb_mem_clear(register VALUE *mem, register long size)
+rb_ary_ptr_use_end(VALUE ary)
+{
+#if ARRAY_DEBUG
+ FL_UNSET_RAW(ary, RARRAY_PTR_IN_USE_FLAG);
+#endif
+}
+
+void
+rb_mem_clear(VALUE *mem, long size)
{
while (size--) {
- *mem++ = Qnil;
+ *mem++ = Qnil;
}
}
static void
ary_mem_clear(VALUE ary, long beg, long size)
{
- RARRAY_PTR_USE(ary, ptr, {
- rb_mem_clear(ptr + beg, size);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ rb_mem_clear(ptr + beg, size);
});
}
@@ -155,44 +347,38 @@ static inline void
memfill(register VALUE *mem, register long size, register VALUE val)
{
while (size--) {
- *mem++ = val;
+ *mem++ = val;
}
}
static void
ary_memfill(VALUE ary, long beg, long size, VALUE val)
{
- RARRAY_PTR_USE(ary, ptr, {
- memfill(ptr + beg, size, val);
- RB_OBJ_WRITTEN(ary, Qundef, val);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ memfill(ptr + beg, size, val);
+ RB_OBJ_WRITTEN(ary, Qundef, val);
});
}
static void
ary_memcpy0(VALUE ary, long beg, long argc, const VALUE *argv, VALUE buff_owner_ary)
{
-#if 1
assert(!ARY_SHARED_P(buff_owner_ary));
if (argc > (int)(128/sizeof(VALUE)) /* is magic number (cache line size) */) {
- rb_gc_writebarrier_remember(buff_owner_ary);
- RARRAY_PTR_USE(ary, ptr, {
- MEMCPY(ptr+beg, argv, VALUE, argc);
- });
+ rb_gc_writebarrier_remember(buff_owner_ary);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMCPY(ptr+beg, argv, VALUE, argc);
+ });
}
else {
- int i;
- RARRAY_PTR_USE(ary, ptr, {
- for (i=0; i<argc; i++) {
- RB_OBJ_WRITE(buff_owner_ary, &ptr[i+beg], argv[i]);
- }
- });
+ int i;
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ for (i=0; i<argc; i++) {
+ RB_OBJ_WRITE(buff_owner_ary, &ptr[i+beg], argv[i]);
+ }
+ });
}
-#else
- /* giveup write barrier (traditional way) */
- RARRAY_PTR(buff_owner_ary);
- MEMCPY(RARRAY_PTR(ary)+beg, argv, VALUE, argc);
-#endif
}
static void
@@ -201,49 +387,198 @@ ary_memcpy(VALUE ary, long beg, long argc, const VALUE *argv)
ary_memcpy0(ary, beg, argc, argv, ary);
}
+static VALUE *
+ary_heap_alloc(VALUE ary, size_t capa)
+{
+ VALUE *ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * capa);
+
+ if (ptr != NULL) {
+ RARY_TRANSIENT_SET(ary);
+ }
+ else {
+ RARY_TRANSIENT_UNSET(ary);
+ ptr = ALLOC_N(VALUE, capa);
+ }
+
+ return ptr;
+}
+
+static void
+ary_heap_free_ptr(VALUE ary, const VALUE *ptr, long size)
+{
+ if (RARRAY_TRANSIENT_P(ary)) {
+ /* ignore it */
+ }
+ else {
+ ruby_sized_xfree((void *)ptr, size);
+ }
+}
+
+static void
+ary_heap_free(VALUE ary)
+{
+ if (RARRAY_TRANSIENT_P(ary)) {
+ RARY_TRANSIENT_UNSET(ary);
+ }
+ else {
+ ary_heap_free_ptr(ary, ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
+ }
+}
+
+static size_t
+ary_heap_realloc(VALUE ary, size_t new_capa)
+{
+ size_t alloc_capa = new_capa;
+ size_t old_capa = ARY_HEAP_CAPA(ary);
+
+ if (RARRAY_TRANSIENT_P(ary)) {
+ if (new_capa <= old_capa) {
+ /* do nothing */
+ alloc_capa = old_capa;
+ }
+ else {
+ VALUE *new_ptr = rb_transient_heap_alloc(ary, sizeof(VALUE) * new_capa);
+
+ if (new_ptr == NULL) {
+ new_ptr = ALLOC_N(VALUE, new_capa);
+ RARY_TRANSIENT_UNSET(ary);
+ }
+
+ MEMCPY(new_ptr, ARY_HEAP_PTR(ary), VALUE, old_capa);
+ ARY_SET_PTR(ary, new_ptr);
+ }
+ }
+ else {
+ SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, new_capa, old_capa);
+ }
+ ary_verify(ary);
+
+ return alloc_capa;
+}
+
+#if USE_TRANSIENT_HEAP
+static inline void
+rb_ary_transient_heap_evacuate_(VALUE ary, int transient, int promote)
+{
+ if (transient) {
+ assert(!ARY_SHARED_ROOT_P(ary));
+
+ VALUE *new_ptr;
+ const VALUE *old_ptr = ARY_HEAP_PTR(ary);
+ long capa = ARY_HEAP_CAPA(ary);
+
+ assert(ARY_OWNS_HEAP_P(ary));
+ assert(RARRAY_TRANSIENT_P(ary));
+ assert(!ARY_PTR_USING_P(ary));
+
+ if (promote) {
+ new_ptr = ALLOC_N(VALUE, capa);
+ RARY_TRANSIENT_UNSET(ary);
+ }
+ else {
+ new_ptr = ary_heap_alloc(ary, capa);
+ }
+
+ MEMCPY(new_ptr, old_ptr, VALUE, capa);
+ /* do not use ARY_SET_PTR() because they assert !frozen */
+ RARRAY(ary)->as.heap.ptr = new_ptr;
+ }
+
+ ary_verify(ary);
+}
+
+void
+rb_ary_transient_heap_evacuate(VALUE ary, int promote)
+{
+ rb_ary_transient_heap_evacuate_(ary, RARRAY_TRANSIENT_P(ary), promote);
+}
+
+void
+rb_ary_detransient(VALUE ary)
+{
+ assert(RARRAY_TRANSIENT_P(ary));
+ rb_ary_transient_heap_evacuate_(ary, TRUE, TRUE);
+}
+#else
+void
+rb_ary_detransient(VALUE ary)
+{
+ /* do nothing */
+}
+#endif
+
+void
+rb_ary_make_embedded(VALUE ary)
+{
+ assert(rb_ary_embeddable_p(ary));
+ if (!ARY_EMBED_P(ary)) {
+ const VALUE *buf = ARY_HEAP_PTR(ary);
+ long len = ARY_HEAP_LEN(ary);
+ bool was_transient = RARRAY_TRANSIENT_P(ary);
+
+ // FL_SET_EMBED also unsets the transient flag
+ FL_SET_EMBED(ary);
+ ARY_SET_EMBED_LEN(ary, len);
+
+ MEMCPY((void *)ARY_EMBED_PTR(ary), (void *)buf, VALUE, len);
+
+ if (!was_transient) {
+ ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
+ }
+ }
+}
+
static void
ary_resize_capa(VALUE ary, long capacity)
{
assert(RARRAY_LEN(ary) <= capacity);
assert(!OBJ_FROZEN(ary));
assert(!ARY_SHARED_P(ary));
- if (capacity > RARRAY_EMBED_LEN_MAX) {
+
+ if (capacity > ary_embed_capa(ary)) {
+ size_t new_capa = capacity;
if (ARY_EMBED_P(ary)) {
long len = ARY_EMBED_LEN(ary);
- VALUE *ptr = ALLOC_N(VALUE, (capacity));
+ VALUE *ptr = ary_heap_alloc(ary, capacity);
+
MEMCPY(ptr, ARY_EMBED_PTR(ary), VALUE, len);
FL_UNSET_EMBED(ary);
ARY_SET_PTR(ary, ptr);
ARY_SET_HEAP_LEN(ary, len);
}
else {
- SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, capacity, RARRAY(ary)->as.heap.aux.capa);
+ new_capa = ary_heap_realloc(ary, capacity);
}
- ARY_SET_CAPA(ary, (capacity));
+ ARY_SET_CAPA(ary, new_capa);
}
else {
if (!ARY_EMBED_P(ary)) {
- long len = RARRAY_LEN(ary);
- const VALUE *ptr = RARRAY_CONST_PTR(ary);
+ long len = ARY_HEAP_LEN(ary);
+ long old_capa = ARY_HEAP_CAPA(ary);
+ const VALUE *ptr = ARY_HEAP_PTR(ary);
- if (len > capacity) len = capacity;
+ if (len > capacity) len = capacity;
MEMCPY((VALUE *)RARRAY(ary)->as.ary, ptr, VALUE, len);
+ ary_heap_free_ptr(ary, ptr, old_capa);
+
FL_SET_EMBED(ary);
ARY_SET_LEN(ary, len);
- ruby_sized_xfree((VALUE *)ptr, RARRAY(ary)->as.heap.aux.capa);
}
}
+
+ ary_verify(ary);
}
static inline void
ary_shrink_capa(VALUE ary)
{
long capacity = ARY_HEAP_LEN(ary);
- long old_capa = RARRAY(ary)->as.heap.aux.capa;
+ long old_capa = ARY_HEAP_CAPA(ary);
assert(!ARY_SHARED_P(ary));
assert(old_capa >= capacity);
- if (old_capa > capacity)
- SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, capacity, old_capa);
+ if (old_capa > capacity) ary_heap_realloc(ary, capacity);
+
+ ary_verify(ary);
}
static void
@@ -252,106 +587,121 @@ ary_double_capa(VALUE ary, long min)
long new_capa = ARY_CAPA(ary) / 2;
if (new_capa < ARY_DEFAULT_SIZE) {
- new_capa = ARY_DEFAULT_SIZE;
+ new_capa = ARY_DEFAULT_SIZE;
}
if (new_capa >= ARY_MAX_SIZE - min) {
- new_capa = (ARY_MAX_SIZE - min) / 2;
+ new_capa = (ARY_MAX_SIZE - min) / 2;
}
new_capa += min;
ary_resize_capa(ary, new_capa);
+
+ ary_verify(ary);
}
static void
-rb_ary_decrement_share(VALUE shared)
+rb_ary_decrement_share(VALUE shared_root)
{
- if (shared) {
- long num = ARY_SHARED_NUM(shared) - 1;
- if (num == 0) {
- rb_ary_free(shared);
- rb_gc_force_recycle(shared);
- }
- else if (num > 0) {
- ARY_SET_SHARED_NUM(shared, num);
- }
+ if (!OBJ_FROZEN(shared_root)) {
+ long num = ARY_SHARED_ROOT_REFCNT(shared_root);
+ ARY_SET_SHARED_ROOT_REFCNT(shared_root, num - 1);
}
}
static void
rb_ary_unshare(VALUE ary)
{
- VALUE shared = RARRAY(ary)->as.heap.aux.shared;
- rb_ary_decrement_share(shared);
+ VALUE shared_root = ARY_SHARED_ROOT(ary);
+ rb_ary_decrement_share(shared_root);
FL_UNSET_SHARED(ary);
}
-static inline void
-rb_ary_unshare_safe(VALUE ary)
+static void
+rb_ary_reset(VALUE ary)
{
- if (ARY_SHARED_P(ary) && !ARY_EMBED_P(ary)) {
- rb_ary_unshare(ary);
+ if (ARY_OWNS_HEAP_P(ary)) {
+ ary_heap_free(ary);
}
+ else if (ARY_SHARED_P(ary)) {
+ rb_ary_unshare(ary);
+ }
+
+ FL_SET_EMBED(ary);
+ ARY_SET_EMBED_LEN(ary, 0);
}
static VALUE
-rb_ary_increment_share(VALUE shared)
+rb_ary_increment_share(VALUE shared_root)
{
- long num = ARY_SHARED_NUM(shared);
- if (num >= 0) {
- ARY_SET_SHARED_NUM(shared, num + 1);
+ if (!OBJ_FROZEN(shared_root)) {
+ long num = ARY_SHARED_ROOT_REFCNT(shared_root);
+ assert(num >= 0);
+ ARY_SET_SHARED_ROOT_REFCNT(shared_root, num + 1);
}
- return shared;
+ return shared_root;
}
static void
-rb_ary_set_shared(VALUE ary, VALUE shared)
+rb_ary_set_shared(VALUE ary, VALUE shared_root)
{
- rb_ary_increment_share(shared);
+ rb_ary_increment_share(shared_root);
FL_SET_SHARED(ary);
- ARY_SET_SHARED(ary, shared);
+ RB_DEBUG_COUNTER_INC(obj_ary_shared_create);
+ ARY_SET_SHARED(ary, shared_root);
}
static inline void
rb_ary_modify_check(VALUE ary)
{
rb_check_frozen(ary);
+ ary_verify(ary);
}
void
-rb_ary_modify(VALUE ary)
+rb_ary_cancel_sharing(VALUE ary)
{
- rb_ary_modify_check(ary);
if (ARY_SHARED_P(ary)) {
- long shared_len, len = RARRAY_LEN(ary);
- VALUE shared = ARY_SHARED(ary);
- if (len <= RARRAY_EMBED_LEN_MAX) {
- const VALUE *ptr = ARY_HEAP_PTR(ary);
+ long shared_len, len = RARRAY_LEN(ary);
+ VALUE shared_root = ARY_SHARED_ROOT(ary);
+
+ ary_verify(shared_root);
+
+ if (len <= ary_embed_capa(ary)) {
+ const VALUE *ptr = ARY_HEAP_PTR(ary);
FL_UNSET_SHARED(ary);
FL_SET_EMBED(ary);
- MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len);
- rb_ary_decrement_share(shared);
+ MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len);
+ rb_ary_decrement_share(shared_root);
ARY_SET_EMBED_LEN(ary, len);
}
- else if (ARY_SHARED_OCCUPIED(shared) && len > ((shared_len = RARRAY_LEN(shared))>>1)) {
- long shift = RARRAY_CONST_PTR(ary) - RARRAY_CONST_PTR(shared);
- FL_UNSET_SHARED(ary);
- ARY_SET_PTR(ary, RARRAY_CONST_PTR(shared));
- ARY_SET_CAPA(ary, shared_len);
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr, ptr+shift, VALUE, len);
- });
- FL_SET_EMBED(shared);
- rb_ary_decrement_share(shared);
- }
+ else if (ARY_SHARED_ROOT_OCCUPIED(shared_root) && len > ((shared_len = RARRAY_LEN(shared_root))>>1)) {
+ long shift = RARRAY_CONST_PTR_TRANSIENT(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root);
+ FL_UNSET_SHARED(ary);
+ ARY_SET_PTR(ary, RARRAY_CONST_PTR_TRANSIENT(shared_root));
+ ARY_SET_CAPA(ary, shared_len);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMMOVE(ptr, ptr+shift, VALUE, len);
+ });
+ FL_SET_EMBED(shared_root);
+ rb_ary_decrement_share(shared_root);
+ }
else {
- VALUE *ptr = ALLOC_N(VALUE, len);
- MEMCPY(ptr, RARRAY_CONST_PTR(ary), VALUE, len);
+ VALUE *ptr = ary_heap_alloc(ary, len);
+ MEMCPY(ptr, ARY_HEAP_PTR(ary), VALUE, len);
rb_ary_unshare(ary);
ARY_SET_CAPA(ary, len);
ARY_SET_PTR(ary, ptr);
}
- rb_gc_writebarrier_remember(ary);
+ rb_gc_writebarrier_remember(ary);
}
+ ary_verify(ary);
+}
+
+void
+rb_ary_modify(VALUE ary)
+{
+ rb_ary_modify_check(ary);
+ rb_ary_cancel_sharing(ary);
}
static VALUE
@@ -362,48 +712,58 @@ ary_ensure_room_for_push(VALUE ary, long add_len)
long capa;
if (old_len > ARY_MAX_SIZE - add_len) {
- rb_raise(rb_eIndexError, "index %ld too big", new_len);
+ rb_raise(rb_eIndexError, "index %ld too big", new_len);
}
if (ARY_SHARED_P(ary)) {
- if (new_len > RARRAY_EMBED_LEN_MAX) {
- VALUE shared = ARY_SHARED(ary);
- if (ARY_SHARED_OCCUPIED(shared)) {
- if (RARRAY_CONST_PTR(ary) - RARRAY_CONST_PTR(shared) + new_len <= RARRAY_LEN(shared)) {
- rb_ary_modify_check(ary);
- return shared;
- }
- else {
- /* if array is shared, then it is likely it participate in push/shift pattern */
- rb_ary_modify(ary);
- capa = ARY_CAPA(ary);
- if (new_len > capa - (capa >> 6)) {
- ary_double_capa(ary, new_len);
- }
- return ary;
- }
- }
- }
- rb_ary_modify(ary);
+ if (new_len > ary_embed_capa(ary)) {
+ VALUE shared_root = ARY_SHARED_ROOT(ary);
+ if (ARY_SHARED_ROOT_OCCUPIED(shared_root)) {
+ if (ARY_HEAP_PTR(ary) - RARRAY_CONST_PTR_TRANSIENT(shared_root) + new_len <= RARRAY_LEN(shared_root)) {
+ rb_ary_modify_check(ary);
+
+ ary_verify(ary);
+ ary_verify(shared_root);
+ return shared_root;
+ }
+ else {
+ /* if array is shared, then it is likely it participate in push/shift pattern */
+ rb_ary_modify(ary);
+ capa = ARY_CAPA(ary);
+ if (new_len > capa - (capa >> 6)) {
+ ary_double_capa(ary, new_len);
+ }
+ ary_verify(ary);
+ return ary;
+ }
+ }
+ }
+ ary_verify(ary);
+ rb_ary_modify(ary);
}
else {
- rb_ary_modify_check(ary);
+ rb_ary_modify_check(ary);
}
capa = ARY_CAPA(ary);
if (new_len > capa) {
- ary_double_capa(ary, new_len);
+ ary_double_capa(ary, new_len);
}
+ ary_verify(ary);
return ary;
}
/*
* call-seq:
- * ary.freeze -> ary
+ * array.freeze -> self
*
- * Calls Object#freeze on +ary+ to prevent any further
- * modification. A RuntimeError will be raised if a modification
- * attempt is made.
+ * Freezes +self+; returns +self+:
*
+ * a = []
+ * a.frozen? # => false
+ * a.freeze
+ * a.frozen? # => true
+ *
+ * An attempt to modify a frozen \Array raises FrozenError.
*/
VALUE
@@ -423,18 +783,25 @@ VALUE
rb_ary_shared_with_p(VALUE ary1, VALUE ary2)
{
if (!ARY_EMBED_P(ary1) && ARY_SHARED_P(ary1) &&
- !ARY_EMBED_P(ary2) && ARY_SHARED_P(ary2) &&
- RARRAY(ary1)->as.heap.aux.shared == RARRAY(ary2)->as.heap.aux.shared &&
- RARRAY(ary1)->as.heap.len == RARRAY(ary2)->as.heap.len) {
- return Qtrue;
+ !ARY_EMBED_P(ary2) && ARY_SHARED_P(ary2) &&
+ ARY_SHARED_ROOT(ary1) == ARY_SHARED_ROOT(ary2) &&
+ ARY_HEAP_LEN(ary1) == ARY_HEAP_LEN(ary2)) {
+ return Qtrue;
}
return Qfalse;
}
static VALUE
-ary_alloc(VALUE klass)
+ary_alloc_embed(VALUE klass, long capa)
{
- NEWOBJ_OF(ary, struct RArray, klass, T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0));
+ size_t size = ary_embed_size(capa);
+ assert(rb_gc_size_allocatable_p(size));
+#if !USE_RVARGC
+ assert(size <= sizeof(struct RArray));
+#endif
+ RVARGC_NEWOBJ_OF(ary, struct RArray, klass,
+ T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0),
+ size);
/* Created array is:
* FL_SET_EMBED((VALUE)ary);
* ARY_SET_EMBED_LEN((VALUE)ary, 0);
@@ -443,10 +810,19 @@ ary_alloc(VALUE klass)
}
static VALUE
+ary_alloc_heap(VALUE klass)
+{
+ RVARGC_NEWOBJ_OF(ary, struct RArray, klass,
+ T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0),
+ sizeof(struct RArray));
+ return (VALUE)ary;
+}
+
+static VALUE
empty_ary_alloc(VALUE klass)
{
RUBY_DTRACE_CREATE_HOOK(ARRAY, 0);
- return ary_alloc(klass);
+ return ary_alloc_embed(klass, 0);
}
static VALUE
@@ -455,20 +831,24 @@ ary_new(VALUE klass, long capa)
VALUE ary,*ptr;
if (capa < 0) {
- rb_raise(rb_eArgError, "negative array size (or size too big)");
+ rb_raise(rb_eArgError, "negative array size (or size too big)");
}
if (capa > ARY_MAX_SIZE) {
- rb_raise(rb_eArgError, "array size too big");
+ rb_raise(rb_eArgError, "array size too big");
}
RUBY_DTRACE_CREATE_HOOK(ARRAY, capa);
- ary = ary_alloc(klass);
- if (capa > RARRAY_EMBED_LEN_MAX) {
- ptr = ALLOC_N(VALUE, capa);
- FL_UNSET_EMBED(ary);
- ARY_SET_PTR(ary, ptr);
+ if (ary_embeddable_p(capa)) {
+ ary = ary_alloc_embed(klass, capa);
+ }
+ else {
+ ary = ary_alloc_heap(klass);
ARY_SET_CAPA(ary, capa);
+ assert(!ARY_EMBED_P(ary));
+
+ ptr = ary_heap_alloc(ary, capa);
+ ARY_SET_PTR(ary, ptr);
ARY_SET_HEAP_LEN(ary, 0);
}
@@ -484,7 +864,7 @@ rb_ary_new_capa(long capa)
VALUE
rb_ary_new(void)
{
- return rb_ary_new2(RARRAY_EMBED_LEN_MAX);
+ return rb_ary_new_capa(0);
}
VALUE
@@ -498,7 +878,7 @@ VALUE
va_start(ar, n);
for (i=0; i<n; i++) {
- ARY_SET(ary, i, va_arg(ar, VALUE));
+ ARY_SET(ary, i, va_arg(ar, VALUE));
}
va_end(ar);
@@ -513,8 +893,8 @@ rb_ary_tmp_new_from_values(VALUE klass, long n, const VALUE *elts)
ary = ary_new(klass, n);
if (n > 0 && elts) {
- ary_memcpy(ary, 0, n, elts);
- ARY_SET_LEN(ary, n);
+ ary_memcpy(ary, 0, n, elts);
+ ARY_SET_LEN(ary, n);
}
return ary;
@@ -526,16 +906,89 @@ rb_ary_new_from_values(long n, const VALUE *elts)
return rb_ary_tmp_new_from_values(rb_cArray, n, elts);
}
+static VALUE
+ec_ary_alloc_embed(rb_execution_context_t *ec, VALUE klass, long capa)
+{
+ size_t size = ary_embed_size(capa);
+ assert(rb_gc_size_allocatable_p(size));
+#if !USE_RVARGC
+ assert(size <= sizeof(struct RArray));
+#endif
+ RB_RVARGC_EC_NEWOBJ_OF(ec, ary, struct RArray, klass,
+ T_ARRAY | RARRAY_EMBED_FLAG | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0),
+ size);
+ /* Created array is:
+ * FL_SET_EMBED((VALUE)ary);
+ * ARY_SET_EMBED_LEN((VALUE)ary, 0);
+ */
+ return (VALUE)ary;
+}
+
+static VALUE
+ec_ary_alloc_heap(rb_execution_context_t *ec, VALUE klass)
+{
+ RB_RVARGC_EC_NEWOBJ_OF(ec, ary, struct RArray, klass,
+ T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0),
+ sizeof(struct RArray));
+ return (VALUE)ary;
+}
+
+static VALUE
+ec_ary_new(rb_execution_context_t *ec, VALUE klass, long capa)
+{
+ VALUE ary,*ptr;
+
+ if (capa < 0) {
+ rb_raise(rb_eArgError, "negative array size (or size too big)");
+ }
+ if (capa > ARY_MAX_SIZE) {
+ rb_raise(rb_eArgError, "array size too big");
+ }
+
+ RUBY_DTRACE_CREATE_HOOK(ARRAY, capa);
+
+ if (ary_embeddable_p(capa)) {
+ ary = ec_ary_alloc_embed(ec, klass, capa);
+ }
+ else {
+ ary = ec_ary_alloc_heap(ec, klass);
+ ARY_SET_CAPA(ary, capa);
+ assert(!ARY_EMBED_P(ary));
+
+ ptr = ary_heap_alloc(ary, capa);
+ ARY_SET_PTR(ary, ptr);
+ ARY_SET_HEAP_LEN(ary, 0);
+ }
+
+ return ary;
+}
+
VALUE
-rb_ary_tmp_new(long capa)
+rb_ec_ary_new_from_values(rb_execution_context_t *ec, long n, const VALUE *elts)
{
- return ary_new(0, capa);
+ VALUE ary;
+
+ ary = ec_ary_new(ec, rb_cArray, n);
+ if (n > 0 && elts) {
+ ary_memcpy(ary, 0, n, elts);
+ ARY_SET_LEN(ary, n);
+ }
+
+ return ary;
}
VALUE
-rb_ary_tmp_new_fill(long capa)
+rb_ary_hidden_new(long capa)
{
VALUE ary = ary_new(0, capa);
+ rb_ary_transient_heap_evacuate(ary, TRUE);
+ return ary;
+}
+
+VALUE
+rb_ary_hidden_new_fill(long capa)
+{
+ VALUE ary = rb_ary_hidden_new(capa);
ary_memfill(ary, 0, capa, Qnil);
ARY_SET_LEN(ary, capa);
return ary;
@@ -545,11 +998,29 @@ void
rb_ary_free(VALUE ary)
{
if (ARY_OWNS_HEAP_P(ary)) {
- RB_DEBUG_COUNTER_INC(obj_ary_ptr);
- ruby_sized_xfree((void *)ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
+ if (USE_DEBUG_COUNTER &&
+ !ARY_SHARED_ROOT_P(ary) &&
+ ARY_HEAP_CAPA(ary) > RARRAY_LEN(ary)) {
+ RB_DEBUG_COUNTER_INC(obj_ary_extracapa);
+ }
+
+ if (RARRAY_TRANSIENT_P(ary)) {
+ RB_DEBUG_COUNTER_INC(obj_ary_transient);
+ }
+ else {
+ RB_DEBUG_COUNTER_INC(obj_ary_ptr);
+ ary_heap_free(ary);
+ }
}
else {
- RB_DEBUG_COUNTER_INC(obj_ary_embed);
+ RB_DEBUG_COUNTER_INC(obj_ary_embed);
+ }
+
+ if (ARY_SHARED_P(ary)) {
+ RB_DEBUG_COUNTER_INC(obj_ary_shared);
+ }
+ if (ARY_SHARED_ROOT_P(ary) && ARY_SHARED_ROOT_OCCUPIED(ary)) {
+ RB_DEBUG_COUNTER_INC(obj_ary_shared_root_occupied);
}
}
@@ -557,51 +1028,69 @@ RUBY_FUNC_EXPORTED size_t
rb_ary_memsize(VALUE ary)
{
if (ARY_OWNS_HEAP_P(ary)) {
- return ARY_CAPA(ary) * sizeof(VALUE);
+ return ARY_CAPA(ary) * sizeof(VALUE);
}
else {
- return 0;
+ return 0;
}
}
-static inline void
-ary_discard(VALUE ary)
-{
- rb_ary_free(ary);
- RBASIC(ary)->flags |= RARRAY_EMBED_FLAG;
- RBASIC(ary)->flags &= ~RARRAY_EMBED_LEN_MASK;
-}
-
static VALUE
ary_make_shared(VALUE ary)
{
- assert(!ARY_EMBED_P(ary));
+ assert(USE_RVARGC || !ARY_EMBED_P(ary));
+ ary_verify(ary);
+
if (ARY_SHARED_P(ary)) {
- return ARY_SHARED(ary);
+ return ARY_SHARED_ROOT(ary);
}
else if (ARY_SHARED_ROOT_P(ary)) {
- return ary;
+ return ary;
}
else if (OBJ_FROZEN(ary)) {
- ary_shrink_capa(ary);
- FL_SET_SHARED_ROOT(ary);
- ARY_SET_SHARED_NUM(ary, 1);
- return ary;
+ if (!ARY_EMBED_P(ary)) {
+ rb_ary_transient_heap_evacuate(ary, TRUE);
+ ary_shrink_capa(ary);
+ }
+ return ary;
}
else {
- long capa = ARY_CAPA(ary), len = RARRAY_LEN(ary);
- NEWOBJ_OF(shared, struct RArray, 0, T_ARRAY | (RGENGC_WB_PROTECTED_ARRAY ? FL_WB_PROTECTED : 0));
- FL_UNSET_EMBED(shared);
+ rb_ary_transient_heap_evacuate(ary, TRUE);
+
+ long capa = ARY_CAPA(ary);
+ long len = RARRAY_LEN(ary);
+
+ /* Shared roots cannot be embedded because the reference count
+ * (refcnt) is stored in as.heap.aux.capa. */
+ VALUE shared = ary_alloc_heap(0);
+ FL_SET_SHARED_ROOT(shared);
+
+ if (ARY_EMBED_P(ary)) {
+ /* Cannot use ary_heap_alloc because we don't want to allocate
+ * on the transient heap. */
+ VALUE *ptr = ALLOC_N(VALUE, capa);
+ ARY_SET_PTR(shared, ptr);
+ ary_memcpy(shared, 0, len, RARRAY_PTR(ary));
- ARY_SET_LEN((VALUE)shared, capa);
- ARY_SET_PTR((VALUE)shared, RARRAY_CONST_PTR(ary));
- ary_mem_clear((VALUE)shared, len, capa - len);
- FL_SET_SHARED_ROOT(shared);
- ARY_SET_SHARED_NUM((VALUE)shared, 1);
- FL_SET_SHARED(ary);
- ARY_SET_SHARED(ary, (VALUE)shared);
- OBJ_FREEZE(shared);
- return (VALUE)shared;
+ FL_UNSET_EMBED(ary);
+ ARY_SET_HEAP_LEN(ary, len);
+ ARY_SET_PTR(ary, ptr);
+ }
+ else {
+ ARY_SET_PTR(shared, RARRAY_PTR(ary));
+ }
+
+ ARY_SET_LEN(shared, capa);
+ ary_mem_clear(shared, len, capa - len);
+ ARY_SET_SHARED_ROOT_REFCNT(shared, 1);
+ FL_SET_SHARED(ary);
+ RB_DEBUG_COUNTER_INC(obj_ary_shared_create);
+ ARY_SET_SHARED(ary, shared);
+
+ ary_verify(shared);
+ ary_verify(ary);
+
+ return shared;
}
}
@@ -610,9 +1099,11 @@ ary_make_substitution(VALUE ary)
{
long len = RARRAY_LEN(ary);
- if (len <= RARRAY_EMBED_LEN_MAX) {
- VALUE subst = rb_ary_new2(len);
- ary_memcpy(subst, 0, len, RARRAY_CONST_PTR(ary));
+ if (ary_embeddable_p(len)) {
+ VALUE subst = rb_ary_new_capa(len);
+ assert(ARY_EMBED_P(subst));
+
+ ary_memcpy(subst, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_EMBED_LEN(subst, len);
return subst;
}
@@ -646,23 +1137,24 @@ rb_check_to_array(VALUE ary)
return rb_check_convert_type_with_id(ary, T_ARRAY, "Array", idTo_a);
}
+VALUE
+rb_to_array(VALUE ary)
+{
+ return rb_convert_type_with_id(ary, T_ARRAY, "Array", idTo_a);
+}
+
/*
* call-seq:
- * Array.try_convert(obj) -> array or nil
+ * Array.try_convert(object) -> object, new_array, or nil
*
- * Tries to convert +obj+ into an array, using +to_ary+ method. Returns the
- * converted array or +nil+ if +obj+ cannot be converted for any reason.
- * This method can be used to check if an argument is an array.
+ * If +object+ is an \Array object, returns +object+.
*
- * Array.try_convert([1]) #=> [1]
- * Array.try_convert("1") #=> nil
+ * Otherwise if +object+ responds to <tt>:to_ary</tt>,
+ * calls <tt>object.to_ary</tt> and returns the result.
*
- * if tmp = Array.try_convert(arg)
- * # the argument is an array
- * elsif tmp = String.try_convert(arg)
- * # the argument is a string
- * end
+ * Returns +nil+ if +object+ does not respond to <tt>:to_ary</tt>
*
+ * Raises an exception unless <tt>object.to_ary</tt> returns an \Array object.
*/
static VALUE
@@ -671,60 +1163,76 @@ rb_ary_s_try_convert(VALUE dummy, VALUE ary)
return rb_check_array_type(ary);
}
+/* :nodoc: */
+static VALUE
+rb_ary_s_new(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE ary;
+
+ if (klass == rb_cArray) {
+ long size = 0;
+ if (argc > 0 && FIXNUM_P(argv[0])) {
+ size = FIX2LONG(argv[0]);
+ if (size < 0) size = 0;
+ }
+
+ ary = ary_new(klass, size);
+
+ rb_obj_call_init_kw(ary, argc, argv, RB_PASS_CALLED_KEYWORDS);
+ }
+ else {
+ ary = rb_class_new_instance_pass_kw(argc, argv, klass);
+ }
+
+ return ary;
+}
+
/*
* call-seq:
- * Array.new(size=0, default=nil)
- * Array.new(array)
- * Array.new(size) {|index| block }
- *
- * Returns a new array.
- *
- * In the first form, if no arguments are sent, the new array will be empty.
- * When a +size+ and an optional +default+ are sent, an array is created with
- * +size+ copies of +default+. Take notice that all elements will reference the
- * same object +default+.
- *
- * The second form creates a copy of the array passed as a parameter (the
- * array is generated by calling to_ary on the parameter).
- *
- * first_array = ["Matz", "Guido"]
+ * Array.new -> new_empty_array
+ * Array.new(array) -> new_array
+ * Array.new(size) -> new_array
+ * Array.new(size, default_value) -> new_array
+ * Array.new(size) {|index| ... } -> new_array
*
- * second_array = Array.new(first_array) #=> ["Matz", "Guido"]
+ * Returns a new \Array.
*
- * first_array.equal? second_array #=> false
+ * With no block and no arguments, returns a new empty \Array object.
*
- * In the last form, an array of the given size is created. Each element in
- * this array is created by passing the element's index to the given block
- * and storing the return value.
+ * With no block and a single \Array argument +array+,
+ * returns a new \Array formed from +array+:
*
- * Array.new(3) {|index| index ** 2}
- * # => [0, 1, 4]
+ * a = Array.new([:foo, 'bar', 2])
+ * a.class # => Array
+ * a # => [:foo, "bar", 2]
*
- * == Common gotchas
+ * With no block and a single \Integer argument +size+,
+ * returns a new \Array of the given size
+ * whose elements are all +nil+:
*
- * When sending the second parameter, the same object will be used as the
- * value for all the array elements:
+ * a = Array.new(3)
+ * a # => [nil, nil, nil]
*
- * a = Array.new(2, Hash.new)
- * # => [{}, {}]
+ * With no block and arguments +size+ and +default_value+,
+ * returns an \Array of the given size;
+ * each element is that same +default_value+:
*
- * a[0]['cat'] = 'feline'
- * a # => [{"cat"=>"feline"}, {"cat"=>"feline"}]
+ * a = Array.new(3, 'x')
+ * a # => ['x', 'x', 'x']
*
- * a[1]['cat'] = 'Felix'
- * a # => [{"cat"=>"Felix"}, {"cat"=>"Felix"}]
+ * With a block and argument +size+,
+ * returns an \Array of the given size;
+ * the block is called with each successive integer +index+;
+ * the element for that +index+ is the return value from the block:
*
- * Since all the Array elements store the same hash, changes to one of them
- * will affect them all.
+ * a = Array.new(3) {|index| "Element #{index}" }
+ * a # => ["Element 0", "Element 1", "Element 2"]
*
- * If multiple copies are what you want, you should use the block
- * version which uses the result of that block each time an element
- * of the array needs to be initialized:
- *
- * a = Array.new(2) {Hash.new}
- * a[0]['cat'] = 'feline'
- * a # => [{"cat"=>"feline"}, {}]
+ * Raises ArgumentError if +size+ is negative.
*
+ * With a block and no argument,
+ * or a single argument +0+,
+ * ignores the block and returns a new empty \Array.
*/
static VALUE
@@ -735,51 +1243,48 @@ rb_ary_initialize(int argc, VALUE *argv, VALUE ary)
rb_ary_modify(ary);
if (argc == 0) {
- if (ARY_OWNS_HEAP_P(ary) && RARRAY_CONST_PTR(ary) != 0) {
- ruby_sized_xfree((void *)RARRAY_CONST_PTR(ary), ARY_HEAP_SIZE(ary));
- }
- rb_ary_unshare_safe(ary);
- FL_SET_EMBED(ary);
- ARY_SET_EMBED_LEN(ary, 0);
- if (rb_block_given_p()) {
- rb_warning("given block not used");
- }
- return ary;
+ rb_ary_reset(ary);
+ assert(ARY_EMBED_P(ary));
+ assert(ARY_EMBED_LEN(ary) == 0);
+ if (rb_block_given_p()) {
+ rb_warning("given block not used");
+ }
+ return ary;
}
rb_scan_args(argc, argv, "02", &size, &val);
if (argc == 1 && !FIXNUM_P(size)) {
- val = rb_check_array_type(size);
- if (!NIL_P(val)) {
- rb_ary_replace(ary, val);
- return ary;
- }
+ val = rb_check_array_type(size);
+ if (!NIL_P(val)) {
+ rb_ary_replace(ary, val);
+ return ary;
+ }
}
len = NUM2LONG(size);
/* NUM2LONG() may call size.to_int, ary can be frozen, modified, etc */
if (len < 0) {
- rb_raise(rb_eArgError, "negative array size");
+ rb_raise(rb_eArgError, "negative array size");
}
if (len > ARY_MAX_SIZE) {
- rb_raise(rb_eArgError, "array size too big");
+ rb_raise(rb_eArgError, "array size too big");
}
/* recheck after argument conversion */
rb_ary_modify(ary);
ary_resize_capa(ary, len);
if (rb_block_given_p()) {
- long i;
+ long i;
- if (argc == 2) {
- rb_warn("block supersedes default value argument");
- }
- for (i=0; i<len; i++) {
- rb_ary_store(ary, i, rb_yield(LONG2NUM(i)));
- ARY_SET_LEN(ary, i + 1);
- }
+ if (argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+ for (i=0; i<len; i++) {
+ rb_ary_store(ary, i, rb_yield(LONG2NUM(i)));
+ ARY_SET_LEN(ary, i + 1);
+ }
}
else {
- ary_memfill(ary, 0, len, val);
- ARY_SET_LEN(ary, len);
+ ary_memfill(ary, 0, len, val);
+ ARY_SET_LEN(ary, len);
}
return ary;
}
@@ -810,26 +1315,26 @@ rb_ary_store(VALUE ary, long idx, VALUE val)
long len = RARRAY_LEN(ary);
if (idx < 0) {
- idx += len;
- if (idx < 0) {
- rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
- idx - len, -len);
- }
+ idx += len;
+ if (idx < 0) {
+ rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
+ idx - len, -len);
+ }
}
else if (idx >= ARY_MAX_SIZE) {
- rb_raise(rb_eIndexError, "index %ld too big", idx);
+ rb_raise(rb_eIndexError, "index %ld too big", idx);
}
rb_ary_modify(ary);
if (idx >= ARY_CAPA(ary)) {
- ary_double_capa(ary, idx);
+ ary_double_capa(ary, idx);
}
if (idx > len) {
- ary_mem_clear(ary, len, idx - len + 1);
+ ary_mem_clear(ary, len, idx - len + 1);
}
if (idx >= len) {
- ARY_SET_LEN(ary, idx + 1);
+ ARY_SET_LEN(ary, idx + 1);
}
ARY_SET(ary, idx, val);
}
@@ -841,31 +1346,87 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
assert(len >= 0);
assert(offset+len <= RARRAY_LEN(ary));
- if (len <= RARRAY_EMBED_LEN_MAX) {
- VALUE result = ary_alloc(klass);
- ary_memcpy(result, 0, len, RARRAY_CONST_PTR(ary) + offset);
+ const size_t rarray_embed_capa_max = (sizeof(struct RArray) - offsetof(struct RArray, as.ary)) / sizeof(VALUE);
+
+ if ((size_t)len <= rarray_embed_capa_max && ary_embeddable_p(len)) {
+ VALUE result = ary_alloc_embed(klass, len);
+ ary_memcpy(result, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary) + offset);
ARY_SET_EMBED_LEN(result, len);
return result;
}
else {
- VALUE shared, result = ary_alloc(klass);
- FL_UNSET_EMBED(result);
+ VALUE shared = ary_make_shared(ary);
+
+ VALUE result = ary_alloc_heap(klass);
+ assert(!ARY_EMBED_P(result));
- shared = ary_make_shared(ary);
- ARY_SET_PTR(result, RARRAY_CONST_PTR(ary));
+ ARY_SET_PTR(result, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_LEN(result, RARRAY_LEN(ary));
rb_ary_set_shared(result, shared);
ARY_INCREASE_PTR(result, offset);
ARY_SET_LEN(result, len);
+
+ ary_verify(shared);
+ ary_verify(result);
+ return result;
+ }
+}
+
+static VALUE
+ary_make_partial_step(VALUE ary, VALUE klass, long offset, long len, long step)
+{
+ assert(offset >= 0);
+ assert(len >= 0);
+ assert(offset+len <= RARRAY_LEN(ary));
+ assert(step != 0);
+
+ const VALUE *values = RARRAY_CONST_PTR_TRANSIENT(ary);
+ const long orig_len = len;
+
+ if (step > 0 && step >= len) {
+ VALUE result = ary_new(klass, 1);
+ VALUE *ptr = (VALUE *)ARY_EMBED_PTR(result);
+ RB_OBJ_WRITE(result, ptr, values[offset]);
+ ARY_SET_EMBED_LEN(result, 1);
return result;
}
+ else if (step < 0 && step < -len) {
+ step = -len;
+ }
+
+ long ustep = (step < 0) ? -step : step;
+ len = roomof(len, ustep);
+
+ long i;
+ long j = offset + ((step > 0) ? 0 : (orig_len - 1));
+
+ VALUE result = ary_new(klass, len);
+ if (ARY_EMBED_P(result)) {
+ VALUE *ptr = (VALUE *)ARY_EMBED_PTR(result);
+ for (i = 0; i < len; ++i) {
+ RB_OBJ_WRITE(result, ptr+i, values[j]);
+ j += step;
+ }
+ ARY_SET_EMBED_LEN(result, len);
+ }
+ else {
+ RARRAY_PTR_USE_TRANSIENT(result, ptr, {
+ for (i = 0; i < len; ++i) {
+ RB_OBJ_WRITE(result, ptr+i, values[j]);
+ j += step;
+ }
+ });
+ ARY_SET_LEN(result, len);
+ }
+
+ return result;
}
static VALUE
ary_make_shared_copy(VALUE ary)
{
- return ary_make_partial(ary, rb_obj_class(ary), 0, RARRAY_LEN(ary));
+ return ary_make_partial(ary, rb_cArray, 0, RARRAY_LEN(ary));
}
enum ary_take_pos_flags
@@ -877,51 +1438,57 @@ enum ary_take_pos_flags
static VALUE
ary_take_first_or_last(int argc, const VALUE *argv, VALUE ary, enum ary_take_pos_flags last)
{
- VALUE nv;
long n;
long len;
long offset = 0;
- rb_scan_args(argc, argv, "1", &nv);
- n = NUM2LONG(nv);
+ argc = rb_check_arity(argc, 0, 1);
+ /* the case optional argument is omitted should be handled in
+ * callers of this function. if another arity case is added,
+ * this arity check needs to rewrite. */
+ RUBY_ASSERT_ALWAYS(argc == 1);
+
+ n = NUM2LONG(argv[0]);
len = RARRAY_LEN(ary);
if (n > len) {
- n = len;
+ n = len;
}
else if (n < 0) {
- rb_raise(rb_eArgError, "negative array size");
+ rb_raise(rb_eArgError, "negative array size");
}
if (last) {
- offset = len - n;
+ offset = len - n;
}
return ary_make_partial(ary, rb_cArray, offset, n);
}
/*
* call-seq:
- * ary << obj -> ary
+ * array << object -> self
+ *
+ * Appends +object+ to +self+; returns +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a << :baz # => [:foo, "bar", 2, :baz]
*
- * Append---Pushes the given object on to the end of this array. This
- * expression returns the array itself, so several appends
- * may be chained together.
+ * Appends +object+ as one element, even if it is another \Array:
*
- * a = [ 1, 2 ]
- * a << "c" << "d" << [ 3, 4 ]
- * #=> [ 1, 2, "c", "d", [ 3, 4 ] ]
- * a
- * #=> [ 1, 2, "c", "d", [ 3, 4 ] ]
+ * a = [:foo, 'bar', 2]
+ * a1 = a << [3, 4]
+ * a1 # => [:foo, "bar", 2, [3, 4]]
*
*/
VALUE
rb_ary_push(VALUE ary, VALUE item)
{
- long idx = RARRAY_LEN(ary);
+ long idx = RARRAY_LEN((ary_verify(ary), ary));
VALUE target_ary = ary_ensure_room_for_push(ary, 1);
- RARRAY_PTR_USE(ary, ptr, {
- RB_OBJ_WRITE(target_ary, &ptr[idx], item);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ RB_OBJ_WRITE(target_ary, &ptr[idx], item);
});
ARY_SET_LEN(ary, idx + 1);
+ ary_verify(ary);
return ary;
}
@@ -937,19 +1504,24 @@ rb_ary_cat(VALUE ary, const VALUE *argv, long len)
/*
* call-seq:
- * ary.push(obj, ...) -> ary
- * ary.append(obj, ...) -> ary
- *
- * Append --- Pushes the given object(s) on to the end of this array. This
- * expression returns the array itself, so several appends
- * may be chained together. See also Array#pop for the opposite
- * effect.
- *
- * a = [ "a", "b", "c" ]
- * a.push("d", "e", "f")
- * #=> ["a", "b", "c", "d", "e", "f"]
- * [1, 2, 3].push(4).push(5)
- * #=> [1, 2, 3, 4, 5]
+ * array.push(*objects) -> self
+ *
+ * Appends trailing elements.
+ *
+ * Appends each argument in +objects+ to +self+; returns +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.push(:baz, :bat) # => [:foo, "bar", 2, :baz, :bat]
+ *
+ * Appends each argument as one element, even if it is another \Array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a1 = a.push([:baz, :bat], [:bam, :bad])
+ * a1 # => [:foo, "bar", 2, [:baz, :bat], [:bam, :bad]]
+ *
+ * Array#append is an alias for Array#push.
+ *
+ * Related: #pop, #shift, #unshift.
*/
static VALUE
@@ -966,32 +1538,46 @@ rb_ary_pop(VALUE ary)
n = RARRAY_LEN(ary);
if (n == 0) return Qnil;
if (ARY_OWNS_HEAP_P(ary) &&
- n * 3 < ARY_CAPA(ary) &&
- ARY_CAPA(ary) > ARY_DEFAULT_SIZE)
+ n * 3 < ARY_CAPA(ary) &&
+ ARY_CAPA(ary) > ARY_DEFAULT_SIZE)
{
- ary_resize_capa(ary, n * 2);
+ ary_resize_capa(ary, n * 2);
}
--n;
ARY_SET_LEN(ary, n);
+ ary_verify(ary);
return RARRAY_AREF(ary, n);
}
/*
* call-seq:
- * ary.pop -> obj or nil
- * ary.pop(n) -> new_ary
+ * array.pop -> object or nil
+ * array.pop(n) -> new_array
+ *
+ * Removes and returns trailing elements.
+ *
+ * When no argument is given and +self+ is not empty,
+ * removes and returns the last element:
*
- * Removes the last element from +self+ and returns it, or
- * +nil+ if the array is empty.
+ * a = [:foo, 'bar', 2]
+ * a.pop # => 2
+ * a # => [:foo, "bar"]
*
- * If a number +n+ is given, returns an array of the last +n+ elements
- * (or less) just like <code>array.slice!(-n, n)</code> does. See also
- * Array#push for the opposite effect.
+ * Returns +nil+ if the array is empty.
*
- * a = [ "a", "b", "c", "d" ]
- * a.pop #=> "d"
- * a.pop(2) #=> ["b", "c"]
- * a #=> ["a"]
+ * When a non-negative \Integer argument +n+ is given and is in range,
+ *
+ * removes and returns the last +n+ elements in a new \Array:
+ * a = [:foo, 'bar', 2]
+ * a.pop(2) # => ["bar", 2]
+ *
+ * If +n+ is positive and out of range,
+ * removes and returns all elements:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.pop(50) # => [:foo, "bar", 2]
+ *
+ * Related: #push, #shift, #unshift.
*/
static VALUE
@@ -1000,12 +1586,13 @@ rb_ary_pop_m(int argc, VALUE *argv, VALUE ary)
VALUE result;
if (argc == 0) {
- return rb_ary_pop(ary);
+ return rb_ary_pop(ary);
}
rb_ary_modify_check(ary);
result = ary_take_first_or_last(argc, argv, ary, ARY_TAKE_LAST);
ARY_INCREASE_LEN(ary, -RARRAY_LEN(result));
+ ary_verify(ary);
return result;
}
@@ -1015,52 +1602,49 @@ rb_ary_shift(VALUE ary)
VALUE top;
long len = RARRAY_LEN(ary);
- rb_ary_modify_check(ary);
- if (len == 0) return Qnil;
+ if (len == 0) {
+ rb_ary_modify_check(ary);
+ return Qnil;
+ }
+
top = RARRAY_AREF(ary, 0);
- if (!ARY_SHARED_P(ary)) {
- if (len < ARY_DEFAULT_SIZE) {
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr, ptr+1, VALUE, len-1);
- }); /* WB: no new reference */
- ARY_INCREASE_LEN(ary, -1);
- return top;
- }
- assert(!ARY_EMBED_P(ary)); /* ARY_EMBED_LEN_MAX < ARY_DEFAULT_SIZE */
-
- ARY_SET(ary, 0, Qnil);
- ary_make_shared(ary);
- }
- else if (ARY_SHARED_OCCUPIED(ARY_SHARED(ary))) {
- RARRAY_PTR_USE(ary, ptr, ptr[0] = Qnil);
- }
- ARY_INCREASE_PTR(ary, 1); /* shift ptr */
- ARY_INCREASE_LEN(ary, -1);
+
+ rb_ary_behead(ary, 1);
return top;
}
/*
* call-seq:
- * ary.shift -> obj or nil
- * ary.shift(n) -> new_ary
+ * array.shift -> object or nil
+ * array.shift(n) -> new_array
+ *
+ * Removes and returns leading elements.
+ *
+ * When no argument is given, removes and returns the first element:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.shift # => :foo
+ * a # => ['bar', 2]
+ *
+ * Returns +nil+ if +self+ is empty.
+ *
+ * When positive \Integer argument +n+ is given, removes the first +n+ elements;
+ * returns those elements in a new \Array:
*
- * Removes the first element of +self+ and returns it (shifting all
- * other elements down by one). Returns +nil+ if the array
- * is empty.
+ * a = [:foo, 'bar', 2]
+ * a.shift(2) # => [:foo, 'bar']
+ * a # => [2]
*
- * If a number +n+ is given, returns an array of the first +n+ elements
- * (or less) just like <code>array.slice!(0, n)</code> does. With +ary+
- * containing only the remainder elements, not including what was shifted to
- * +new_ary+. See also Array#unshift for the opposite effect.
+ * If +n+ is as large as or larger than <tt>self.length</tt>,
+ * removes all elements; returns those elements in a new \Array:
*
- * args = [ "-m", "-q", "filename" ]
- * args.shift #=> "-m"
- * args #=> ["-q", "filename"]
+ * a = [:foo, 'bar', 2]
+ * a.shift(3) # => [:foo, 'bar', 2]
*
- * args = [ "-m", "-q", "filename" ]
- * args.shift(2) #=> ["-m", "-q"]
- * args #=> ["filename"]
+ * If +n+ is zero, returns a new empty \Array; +self+ is unmodified.
+ *
+ * Related: #push, #pop, #unshift.
*/
static VALUE
@@ -1070,7 +1654,7 @@ rb_ary_shift_m(int argc, VALUE *argv, VALUE ary)
long n;
if (argc == 0) {
- return rb_ary_shift(ary);
+ return rb_ary_shift(ary);
}
rb_ary_modify_check(ary);
@@ -1084,102 +1668,133 @@ rb_ary_shift_m(int argc, VALUE *argv, VALUE ary)
MJIT_FUNC_EXPORTED VALUE
rb_ary_behead(VALUE ary, long n)
{
- if(n<=0) return ary;
+ if (n <= 0) {
+ return ary;
+ }
rb_ary_modify_check(ary);
- if (ARY_SHARED_P(ary)) {
- if (ARY_SHARED_OCCUPIED(ARY_SHARED(ary))) {
- setup_occupied_shared:
- ary_mem_clear(ary, 0, n);
- }
- ARY_INCREASE_PTR(ary, n);
+
+ if (!ARY_SHARED_P(ary)) {
+ if (ARY_EMBED_P(ary) || RARRAY_LEN(ary) < ARY_DEFAULT_SIZE) {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMMOVE(ptr, ptr + n, VALUE, RARRAY_LEN(ary) - n);
+ }); /* WB: no new reference */
+ ARY_INCREASE_LEN(ary, -n);
+ ary_verify(ary);
+ return ary;
+ }
+
+ ary_mem_clear(ary, 0, n);
+ ary_make_shared(ary);
}
- else {
- if (RARRAY_LEN(ary) < ARY_DEFAULT_SIZE) {
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr, ptr+n, VALUE, RARRAY_LEN(ary)-n);
- }); /* WB: no new reference */
- }
- else {
- ary_make_shared(ary);
- goto setup_occupied_shared;
- }
+ else if (ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary))) {
+ ary_mem_clear(ary, 0, n);
}
+
+ ARY_INCREASE_PTR(ary, n);
ARY_INCREASE_LEN(ary, -n);
+ ary_verify(ary);
return ary;
}
static VALUE
-ary_ensure_room_for_unshift(VALUE ary, int argc)
+make_room_for_unshift(VALUE ary, const VALUE *head, VALUE *sharedp, int argc, long capa, long len)
+{
+ if (head - sharedp < argc) {
+ long room = capa - len - argc;
+
+ room -= room >> 4;
+ MEMMOVE((VALUE *)sharedp + argc + room, head, VALUE, len);
+ head = sharedp + argc + room;
+ }
+ ARY_SET_PTR(ary, head - argc);
+ assert(ARY_SHARED_ROOT_OCCUPIED(ARY_SHARED_ROOT(ary)));
+
+ ary_verify(ary);
+ return ARY_SHARED_ROOT(ary);
+}
+
+static VALUE
+ary_modify_for_unshift(VALUE ary, int argc)
{
long len = RARRAY_LEN(ary);
long new_len = len + argc;
long capa;
const VALUE *head, *sharedp;
- if (len > ARY_MAX_SIZE - argc) {
- rb_raise(rb_eIndexError, "index %ld too big", new_len);
- }
-
- if (ARY_SHARED_P(ary)) {
- VALUE shared = ARY_SHARED(ary);
- capa = RARRAY_LEN(shared);
- if (ARY_SHARED_OCCUPIED(shared) && capa > new_len) {
- head = RARRAY_CONST_PTR(ary);
- sharedp = RARRAY_CONST_PTR(shared);
- goto makeroom_if_need;
- }
- }
-
rb_ary_modify(ary);
capa = ARY_CAPA(ary);
if (capa - (capa >> 6) <= new_len) {
- ary_double_capa(ary, new_len);
+ ary_double_capa(ary, new_len);
}
/* use shared array for big "queues" */
- if (new_len > ARY_DEFAULT_SIZE * 4) {
- /* make a room for unshifted items */
- capa = ARY_CAPA(ary);
- ary_make_shared(ary);
-
- head = sharedp = RARRAY_CONST_PTR(ary);
- goto makeroom;
- makeroom_if_need:
- if (head - sharedp < argc) {
- long room;
- makeroom:
- room = capa - new_len;
- room -= room >> 4;
- MEMMOVE((VALUE *)sharedp + argc + room, head, VALUE, len);
- head = sharedp + argc + room;
- }
- ARY_SET_PTR(ary, head - argc);
- assert(ARY_SHARED_OCCUPIED(ARY_SHARED(ary)));
- return ARY_SHARED(ary);
+ if (new_len > ARY_DEFAULT_SIZE * 4 && !ARY_EMBED_P(ary)) {
+ ary_verify(ary);
+
+ /* make a room for unshifted items */
+ capa = ARY_CAPA(ary);
+ ary_make_shared(ary);
+
+ head = sharedp = RARRAY_CONST_PTR_TRANSIENT(ary);
+ return make_room_for_unshift(ary, head, (void *)sharedp, argc, capa, len);
+ }
+ else {
+ /* sliding items */
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMMOVE(ptr + argc, ptr, VALUE, len);
+ });
+
+ ary_verify(ary);
+ return ary;
+ }
+}
+
+static VALUE
+ary_ensure_room_for_unshift(VALUE ary, int argc)
+{
+ long len = RARRAY_LEN(ary);
+ long new_len = len + argc;
+
+ if (len > ARY_MAX_SIZE - argc) {
+ rb_raise(rb_eIndexError, "index %ld too big", new_len);
+ }
+ else if (! ARY_SHARED_P(ary)) {
+ return ary_modify_for_unshift(ary, argc);
}
else {
- /* sliding items */
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr + argc, ptr, VALUE, len);
- });
+ VALUE shared_root = ARY_SHARED_ROOT(ary);
+ long capa = RARRAY_LEN(shared_root);
- return ary;
+ if (! ARY_SHARED_ROOT_OCCUPIED(shared_root)) {
+ return ary_modify_for_unshift(ary, argc);
+ }
+ else if (new_len > capa) {
+ return ary_modify_for_unshift(ary, argc);
+ }
+ else {
+ const VALUE * head = RARRAY_CONST_PTR_TRANSIENT(ary);
+ void *sharedp = (void *)RARRAY_CONST_PTR_TRANSIENT(shared_root);
+
+ rb_ary_modify_check(ary);
+ return make_room_for_unshift(ary, head, sharedp, argc, capa, len);
+ }
}
}
/*
* call-seq:
- * ary.unshift(obj, ...) -> ary
- * ary.prepend(obj, ...) -> ary
+ * array.unshift(*objects) -> self
+ *
+ * Prepends the given +objects+ to +self+:
*
- * Prepends objects to the front of +self+, moving other elements upwards.
- * See also Array#shift for the opposite effect.
+ * a = [:foo, 'bar', 2]
+ * a.unshift(:bam, :bat) # => [:bam, :bat, :foo, "bar", 2]
*
- * a = [ "b", "c", "d" ]
- * a.unshift("a") #=> ["a", "b", "c", "d"]
- * a.unshift(1, 2) #=> [ 1, 2, "a", "b", "c", "d"]
+ * Array#prepend is an alias for Array#unshift.
+ *
+ * Related: #push, #pop, #shift.
*/
static VALUE
@@ -1189,8 +1804,8 @@ rb_ary_unshift_m(int argc, VALUE *argv, VALUE ary)
VALUE target_ary;
if (argc == 0) {
- rb_ary_modify_check(ary);
- return ary;
+ rb_ary_modify_check(ary);
+ return ary;
}
target_ary = ary_ensure_room_for_unshift(ary, argc);
@@ -1212,7 +1827,7 @@ rb_ary_elt(VALUE ary, long offset)
long len = RARRAY_LEN(ary);
if (len == 0) return Qnil;
if (offset < 0 || len <= offset) {
- return Qnil;
+ return Qnil;
}
return RARRAY_AREF(ary, offset);
}
@@ -1224,7 +1839,7 @@ rb_ary_entry(VALUE ary, long offset)
}
VALUE
-rb_ary_subseq(VALUE ary, long beg, long len)
+rb_ary_subseq_step(VALUE ary, long beg, long len, long step)
{
VALUE klass;
long alen = RARRAY_LEN(ary);
@@ -1233,48 +1848,129 @@ rb_ary_subseq(VALUE ary, long beg, long len)
if (beg < 0 || len < 0) return Qnil;
if (alen < len || alen < beg + len) {
- len = alen - beg;
+ len = alen - beg;
}
- klass = rb_obj_class(ary);
+ klass = rb_cArray;
if (len == 0) return ary_new(klass, 0);
+ if (step == 0)
+ rb_raise(rb_eArgError, "slice step cannot be zero");
+ if (step == 1)
+ return ary_make_partial(ary, klass, beg, len);
+ else
+ return ary_make_partial_step(ary, klass, beg, len, step);
+}
- return ary_make_partial(ary, klass, beg, len);
+VALUE
+rb_ary_subseq(VALUE ary, long beg, long len)
+{
+ return rb_ary_subseq_step(ary, beg, len, 1);
}
+static VALUE rb_ary_aref2(VALUE ary, VALUE b, VALUE e);
+
/*
* call-seq:
- * ary[index] -> obj or nil
- * ary[start, length] -> new_ary or nil
- * ary[range] -> new_ary or nil
- * ary.slice(index) -> obj or nil
- * ary.slice(start, length) -> new_ary or nil
- * ary.slice(range) -> new_ary or nil
- *
- * Element Reference --- Returns the element at +index+, or returns a
- * subarray starting at the +start+ index and continuing for +length+
- * elements, or returns a subarray specified by +range+ of indices.
- *
- * Negative indices count backward from the end of the array (-1 is the last
- * element). For +start+ and +range+ cases the starting index is just before
- * an element. Additionally, an empty array is returned when the starting
- * index for an element range is at the end of the array.
- *
- * Returns +nil+ if the index (or starting index) are out of range.
- *
- * a = [ "a", "b", "c", "d", "e" ]
- * a[2] + a[0] + a[1] #=> "cab"
- * a[6] #=> nil
- * a[1, 2] #=> [ "b", "c" ]
- * a[1..3] #=> [ "b", "c", "d" ]
- * a[4..7] #=> [ "e" ]
- * a[6..10] #=> nil
- * a[-3, 3] #=> [ "c", "d", "e" ]
- * # special cases
- * a[5] #=> nil
- * a[6, 1] #=> nil
- * a[5, 1] #=> []
- * a[5..10] #=> []
+ * array[index] -> object or nil
+ * array[start, length] -> object or nil
+ * array[range] -> object or nil
+ * array[aseq] -> object or nil
+ * array.slice(index) -> object or nil
+ * array.slice(start, length) -> object or nil
+ * array.slice(range) -> object or nil
+ * array.slice(aseq) -> object or nil
+ *
+ * Returns elements from +self+; does not modify +self+.
+ *
+ * When a single \Integer argument +index+ is given, returns the element at offset +index+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0] # => :foo
+ * a[2] # => 2
+ * a # => [:foo, "bar", 2]
+ *
+ * If +index+ is negative, counts relative to the end of +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[-1] # => 2
+ * a[-2] # => "bar"
+ *
+ * If +index+ is out of range, returns +nil+.
+ *
+ * When two \Integer arguments +start+ and +length+ are given,
+ * returns a new \Array of size +length+ containing successive elements beginning at offset +start+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0, 2] # => [:foo, "bar"]
+ * a[1, 2] # => ["bar", 2]
+ *
+ * If <tt>start + length</tt> is greater than <tt>self.length</tt>,
+ * returns all elements from offset +start+ to the end:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0, 4] # => [:foo, "bar", 2]
+ * a[1, 3] # => ["bar", 2]
+ * a[2, 2] # => [2]
+ *
+ * If <tt>start == self.size</tt> and <tt>length >= 0</tt>,
+ * returns a new empty \Array.
+ *
+ * If +length+ is negative, returns +nil+.
+ *
+ * When a single \Range argument +range+ is given,
+ * treats <tt>range.min</tt> as +start+ above
+ * and <tt>range.size</tt> as +length+ above:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0..1] # => [:foo, "bar"]
+ * a[1..2] # => ["bar", 2]
+ *
+ * Special case: If <tt>range.start == a.size</tt>, returns a new empty \Array.
+ *
+ * If <tt>range.end</tt> is negative, calculates the end index from the end:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0..-1] # => [:foo, "bar", 2]
+ * a[0..-2] # => [:foo, "bar"]
+ * a[0..-3] # => [:foo]
+ *
+ * If <tt>range.start</tt> is negative, calculates the start index from the end:
*
+ * a = [:foo, 'bar', 2]
+ * a[-1..2] # => [2]
+ * a[-2..2] # => ["bar", 2]
+ * a[-3..2] # => [:foo, "bar", 2]
+ *
+ * If <tt>range.start</tt> is larger than the array size, returns +nil+.
+ *
+ * a = [:foo, 'bar', 2]
+ * a[4..1] # => nil
+ * a[4..0] # => nil
+ * a[4..-1] # => nil
+ *
+ * When a single Enumerator::ArithmeticSequence argument +aseq+ is given,
+ * returns an \Array of elements corresponding to the indexes produced by
+ * the sequence.
+ *
+ * a = ['--', 'data1', '--', 'data2', '--', 'data3']
+ * a[(1..).step(2)] # => ["data1", "data2", "data3"]
+ *
+ * Unlike slicing with range, if the start or the end of the arithmetic sequence
+ * is larger than array size, throws RangeError.
+ *
+ * a = ['--', 'data1', '--', 'data2', '--', 'data3']
+ * a[(1..11).step(2)]
+ * # RangeError (((1..11).step(2)) out of range)
+ * a[(7..).step(2)]
+ * # RangeError (((7..).step(2)) out of range)
+ *
+ * If given a single argument, and its type is not one of the listed, tries to
+ * convert it to Integer, and raises if it is impossible:
+ *
+ * a = [:foo, 'bar', 2]
+ * # Raises TypeError (no implicit conversion of Symbol into Integer):
+ * a[:foo]
+ *
+ * Array#slice is an alias for Array#[].
*/
VALUE
@@ -1282,18 +1978,18 @@ rb_ary_aref(int argc, const VALUE *argv, VALUE ary)
{
rb_check_arity(argc, 1, 2);
if (argc == 2) {
- return rb_ary_aref2(ary, argv[0], argv[1]);
+ return rb_ary_aref2(ary, argv[0], argv[1]);
}
return rb_ary_aref1(ary, argv[0]);
}
-VALUE
+static VALUE
rb_ary_aref2(VALUE ary, VALUE b, VALUE e)
{
long beg = NUM2LONG(b);
long len = NUM2LONG(e);
if (beg < 0) {
- beg += RARRAY_LEN(ary);
+ beg += RARRAY_LEN(ary);
}
return rb_ary_subseq(ary, beg, len);
}
@@ -1301,35 +1997,34 @@ rb_ary_aref2(VALUE ary, VALUE b, VALUE e)
MJIT_FUNC_EXPORTED VALUE
rb_ary_aref1(VALUE ary, VALUE arg)
{
- long beg, len;
+ long beg, len, step;
/* special case - speeding up */
if (FIXNUM_P(arg)) {
- return rb_ary_entry(ary, FIX2LONG(arg));
+ return rb_ary_entry(ary, FIX2LONG(arg));
}
- /* check if idx is Range */
- switch (rb_range_beg_len(arg, &beg, &len, RARRAY_LEN(ary), 0)) {
+ /* check if idx is Range or ArithmeticSequence */
+ switch (rb_arithmetic_sequence_beg_len_step(arg, &beg, &len, &step, RARRAY_LEN(ary), 0)) {
case Qfalse:
- break;
+ break;
case Qnil:
- return Qnil;
+ return Qnil;
default:
- return rb_ary_subseq(ary, beg, len);
+ return rb_ary_subseq_step(ary, beg, len, step);
}
+
return rb_ary_entry(ary, NUM2LONG(arg));
}
/*
* call-seq:
- * ary.at(index) -> obj or nil
+ * array.at(index) -> object
*
- * Returns the element at +index+. A negative index counts from the end of
- * +self+. Returns +nil+ if the index is out of range. See also
- * Array#[].
+ * Returns the element at \Integer offset +index+; does not modify +self+.
+ * a = [:foo, 'bar', 2]
+ * a.at(0) # => :foo
+ * a.at(2) # => 2
*
- * a = [ "a", "b", "c", "d", "e" ]
- * a.at(0) #=> "a"
- * a.at(-1) #=> "e"
*/
VALUE
@@ -1340,81 +2035,131 @@ rb_ary_at(VALUE ary, VALUE pos)
/*
* call-seq:
- * ary.first -> obj or nil
- * ary.first(n) -> new_ary
+ * array.first -> object or nil
+ * array.first(n) -> new_array
+ *
+ * Returns elements from +self+; does not modify +self+.
+ *
+ * When no argument is given, returns the first element:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.first # => :foo
+ * a # => [:foo, "bar", 2]
+ *
+ * If +self+ is empty, returns +nil+.
*
- * Returns the first element, or the first +n+ elements, of the array.
- * If the array is empty, the first form returns +nil+, and the
- * second form returns an empty array. See also Array#last for
- * the opposite effect.
+ * When non-negative \Integer argument +n+ is given,
+ * returns the first +n+ elements in a new \Array:
*
- * a = [ "q", "r", "s", "t" ]
- * a.first #=> "q"
- * a.first(2) #=> ["q", "r"]
+ * a = [:foo, 'bar', 2]
+ * a.first(2) # => [:foo, "bar"]
+ *
+ * If <tt>n >= array.size</tt>, returns all elements:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.first(50) # => [:foo, "bar", 2]
+ *
+ * If <tt>n == 0</tt> returns an new empty \Array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.first(0) # []
+ *
+ * Related: #last.
*/
-
static VALUE
rb_ary_first(int argc, VALUE *argv, VALUE ary)
{
if (argc == 0) {
- if (RARRAY_LEN(ary) == 0) return Qnil;
- return RARRAY_AREF(ary, 0);
+ if (RARRAY_LEN(ary) == 0) return Qnil;
+ return RARRAY_AREF(ary, 0);
}
else {
- return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_FIRST);
+ return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_FIRST);
}
}
/*
* call-seq:
- * ary.last -> obj or nil
- * ary.last(n) -> new_ary
+ * array.last -> object or nil
+ * array.last(n) -> new_array
*
- * Returns the last element(s) of +self+. If the array is empty,
- * the first form returns +nil+.
+ * Returns elements from +self+; +self+ is not modified.
*
- * See also Array#first for the opposite effect.
+ * When no argument is given, returns the last element:
*
- * a = [ "w", "x", "y", "z" ]
- * a.last #=> "z"
- * a.last(2) #=> ["y", "z"]
+ * a = [:foo, 'bar', 2]
+ * a.last # => 2
+ * a # => [:foo, "bar", 2]
+ *
+ * If +self+ is empty, returns +nil+.
+ *
+ * When non-negative \Integer argument +n+ is given,
+ * returns the last +n+ elements in a new \Array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.last(2) # => ["bar", 2]
+ *
+ * If <tt>n >= array.size</tt>, returns all elements:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.last(50) # => [:foo, "bar", 2]
+ *
+ * If <tt>n == 0</tt>, returns an new empty \Array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.last(0) # []
+ *
+ * Related: #first.
*/
VALUE
rb_ary_last(int argc, const VALUE *argv, VALUE ary)
{
if (argc == 0) {
- long len = RARRAY_LEN(ary);
- if (len == 0) return Qnil;
- return RARRAY_AREF(ary, len-1);
+ long len = RARRAY_LEN(ary);
+ if (len == 0) return Qnil;
+ return RARRAY_AREF(ary, len-1);
}
else {
- return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_LAST);
+ return ary_take_first_or_last(argc, argv, ary, ARY_TAKE_LAST);
}
}
/*
* call-seq:
- * ary.fetch(index) -> obj
- * ary.fetch(index, default) -> obj
- * ary.fetch(index) {|index| block} -> obj
- *
- * Tries to return the element at position +index+, but throws an IndexError
- * exception if the referenced +index+ lies outside of the array bounds. This
- * error can be prevented by supplying a second argument, which will act as a
- * +default+ value.
- *
- * Alternatively, if a block is given it will only be executed when an
- * invalid +index+ is referenced.
- *
- * Negative values of +index+ count from the end of the array.
- *
- * a = [ 11, 22, 33, 44 ]
- * a.fetch(1) #=> 22
- * a.fetch(-1) #=> 44
- * a.fetch(4, 'cat') #=> "cat"
- * a.fetch(100) {|i| puts "#{i} is out of bounds"}
- * #=> "100 is out of bounds"
+ * array.fetch(index) -> element
+ * array.fetch(index, default_value) -> element
+ * array.fetch(index) {|index| ... } -> element
+ *
+ * Returns the element at offset +index+.
+ *
+ * With the single \Integer argument +index+,
+ * returns the element at offset +index+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.fetch(1) # => "bar"
+ *
+ * If +index+ is negative, counts from the end of the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.fetch(-1) # => 2
+ * a.fetch(-2) # => "bar"
+ *
+ * With arguments +index+ and +default_value+,
+ * returns the element at offset +index+ if index is in range,
+ * otherwise returns +default_value+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.fetch(1, nil) # => "bar"
+ *
+ * With argument +index+ and a block,
+ * returns the element at offset +index+ if index is in range
+ * (and the block is not called); otherwise calls the block with index and returns its return value:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.fetch(1) {|index| raise 'Cannot happen' } # => "bar"
+ * a.fetch(50) {|index| "Value for #{index}" } # => "Value for 50"
+ *
*/
static VALUE
@@ -1427,48 +2172,60 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary)
rb_scan_args(argc, argv, "11", &pos, &ifnone);
block_given = rb_block_given_p();
if (block_given && argc == 2) {
- rb_warn("block supersedes default value argument");
+ rb_warn("block supersedes default value argument");
}
idx = NUM2LONG(pos);
if (idx < 0) {
- idx += RARRAY_LEN(ary);
+ idx += RARRAY_LEN(ary);
}
if (idx < 0 || RARRAY_LEN(ary) <= idx) {
- if (block_given) return rb_yield(pos);
- if (argc == 1) {
- rb_raise(rb_eIndexError, "index %ld outside of array bounds: %ld...%ld",
- idx - (idx < 0 ? RARRAY_LEN(ary) : 0), -RARRAY_LEN(ary), RARRAY_LEN(ary));
- }
- return ifnone;
+ if (block_given) return rb_yield(pos);
+ if (argc == 1) {
+ rb_raise(rb_eIndexError, "index %ld outside of array bounds: %ld...%ld",
+ idx - (idx < 0 ? RARRAY_LEN(ary) : 0), -RARRAY_LEN(ary), RARRAY_LEN(ary));
+ }
+ return ifnone;
}
return RARRAY_AREF(ary, idx);
}
/*
* call-seq:
- * ary.find_index(obj) -> int or nil
- * ary.find_index {|item| block} -> int or nil
- * ary.find_index -> Enumerator
- * ary.index(obj) -> int or nil
- * ary.index {|item| block} -> int or nil
- * ary.index -> Enumerator
+ * array.index(object) -> integer or nil
+ * array.index {|element| ... } -> integer or nil
+ * array.index -> new_enumerator
+ *
+ * Returns the index of a specified element.
+ *
+ * When argument +object+ is given but no block,
+ * returns the index of the first element +element+
+ * for which <tt>object == element</tt>:
+ *
+ * a = [:foo, 'bar', 2, 'bar']
+ * a.index('bar') # => 1
+ *
+ * Returns +nil+ if no such element found.
+ *
+ * When both argument +object+ and a block are given,
+ * calls the block with each successive element;
+ * returns the index of the first element for which the block returns a truthy value:
+ *
+ * a = [:foo, 'bar', 2, 'bar']
+ * a.index {|element| element == 'bar' } # => 1
*
- * Returns the _index_ of the first object in +ary+ such that the object is
- * <code>==</code> to +obj+.
+ * Returns +nil+ if the block never returns a truthy value.
*
- * If a block is given instead of an argument, returns the _index_ of the
- * first object for which the block returns +true+. Returns +nil+ if no
- * match is found.
+ * When neither an argument nor a block is given, returns a new Enumerator:
*
- * See also Array#rindex.
+ * a = [:foo, 'bar', 2]
+ * e = a.index
+ * e # => #<Enumerator: [:foo, "bar", 2]:index>
+ * e.each {|element| element == 'bar' } # => 1
*
- * An Enumerator is returned if neither a block nor argument is given.
+ * Array#find_index is an alias for Array#index.
*
- * a = [ "a", "b", "c" ]
- * a.index("b") #=> 1
- * a.index("z") #=> nil
- * a.index {|x| x == "b"} #=> 1
+ * Related: #rindex.
*/
static VALUE
@@ -1478,49 +2235,58 @@ rb_ary_index(int argc, VALUE *argv, VALUE ary)
long i;
if (argc == 0) {
- RETURN_ENUMERATOR(ary, 0, 0);
- for (i=0; i<RARRAY_LEN(ary); i++) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
- return LONG2NUM(i);
- }
- }
- return Qnil;
+ RETURN_ENUMERATOR(ary, 0, 0);
+ for (i=0; i<RARRAY_LEN(ary); i++) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
+ return LONG2NUM(i);
+ }
+ }
+ return Qnil;
}
rb_check_arity(argc, 0, 1);
val = argv[0];
if (rb_block_given_p())
- rb_warn("given block not used");
+ rb_warn("given block not used");
for (i=0; i<RARRAY_LEN(ary); i++) {
- VALUE e = RARRAY_AREF(ary, i);
- if (rb_equal(e, val)) {
- return LONG2NUM(i);
- }
+ VALUE e = RARRAY_AREF(ary, i);
+ if (rb_equal(e, val)) {
+ return LONG2NUM(i);
+ }
}
return Qnil;
}
/*
* call-seq:
- * ary.rindex(obj) -> int or nil
- * ary.rindex {|item| block} -> int or nil
- * ary.rindex -> Enumerator
+ * array.rindex(object) -> integer or nil
+ * array.rindex {|element| ... } -> integer or nil
+ * array.rindex -> new_enumerator
+ *
+ * Returns the index of the last element for which <tt>object == element</tt>.
+ *
+ * When argument +object+ is given but no block, returns the index of the last such element found:
*
- * Returns the _index_ of the last object in +self+ <code>==</code> to +obj+.
+ * a = [:foo, 'bar', 2, 'bar']
+ * a.rindex('bar') # => 3
*
- * If a block is given instead of an argument, returns the _index_ of the
- * first object for which the block returns +true+, starting from the last
- * object.
+ * Returns +nil+ if no such object found.
*
- * Returns +nil+ if no match is found.
+ * When a block is given but no argument, calls the block with each successive element;
+ * returns the index of the last element for which the block returns a truthy value:
*
- * See also Array#index.
+ * a = [:foo, 'bar', 2, 'bar']
+ * a.rindex {|element| element == 'bar' } # => 3
*
- * If neither block nor argument is given, an Enumerator is returned instead.
+ * Returns +nil+ if the block never returns a truthy value.
*
- * a = [ "a", "b", "b", "b", "c" ]
- * a.rindex("b") #=> 3
- * a.rindex("z") #=> nil
- * a.rindex {|x| x == "b"} #=> 3
+ * When neither an argument nor a block is given, returns a new \Enumerator:
+ *
+ * a = [:foo, 'bar', 2, 'bar']
+ * e = a.rindex
+ * e # => #<Enumerator: [:foo, "bar", 2, "bar"]:rindex>
+ * e.each {|element| element == 'bar' } # => 3
+ *
+ * Related: #index.
*/
static VALUE
@@ -1530,25 +2296,28 @@ rb_ary_rindex(int argc, VALUE *argv, VALUE ary)
long i = RARRAY_LEN(ary), len;
if (argc == 0) {
- RETURN_ENUMERATOR(ary, 0, 0);
- while (i--) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i))))
- return LONG2NUM(i);
- if (i > (len = RARRAY_LEN(ary))) {
- i = len;
- }
- }
- return Qnil;
+ RETURN_ENUMERATOR(ary, 0, 0);
+ while (i--) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i))))
+ return LONG2NUM(i);
+ if (i > (len = RARRAY_LEN(ary))) {
+ i = len;
+ }
+ }
+ return Qnil;
}
rb_check_arity(argc, 0, 1);
val = argv[0];
if (rb_block_given_p())
- rb_warn("given block not used");
+ rb_warn("given block not used");
while (i--) {
- VALUE e = RARRAY_AREF(ary, i);
- if (rb_equal(e, val)) {
- return LONG2NUM(i);
- }
+ VALUE e = RARRAY_AREF(ary, i);
+ if (rb_equal(e, val)) {
+ return LONG2NUM(i);
+ }
+ if (i > RARRAY_LEN(ary)) {
+ break;
+ }
}
return Qnil;
}
@@ -1571,57 +2340,64 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
olen = RARRAY_LEN(ary);
if (beg < 0) {
- beg += olen;
- if (beg < 0) {
- rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
- beg - olen, -olen);
- }
+ beg += olen;
+ if (beg < 0) {
+ rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
+ beg - olen, -olen);
+ }
}
if (olen < len || olen < beg + len) {
- len = olen - beg;
+ len = olen - beg;
}
{
- const VALUE *optr = RARRAY_CONST_PTR(ary);
- rofs = (rptr >= optr && rptr < optr + olen) ? rptr - optr : -1;
+ const VALUE *optr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ rofs = (rptr >= optr && rptr < optr + olen) ? rptr - optr : -1;
}
if (beg >= olen) {
- VALUE target_ary;
- if (beg > ARY_MAX_SIZE - rlen) {
- rb_raise(rb_eIndexError, "index %ld too big", beg);
- }
- target_ary = ary_ensure_room_for_push(ary, rlen-len); /* len is 0 or negative */
- len = beg + rlen;
- ary_mem_clear(ary, olen, beg - olen);
- if (rlen > 0) {
- if (rofs != -1) rptr = RARRAY_CONST_PTR(ary) + rofs;
- ary_memcpy0(ary, beg, rlen, rptr, target_ary);
- }
- ARY_SET_LEN(ary, len);
+ VALUE target_ary;
+ if (beg > ARY_MAX_SIZE - rlen) {
+ rb_raise(rb_eIndexError, "index %ld too big", beg);
+ }
+ target_ary = ary_ensure_room_for_push(ary, rlen-len); /* len is 0 or negative */
+ len = beg + rlen;
+ ary_mem_clear(ary, olen, beg - olen);
+ if (rlen > 0) {
+ if (rofs != -1) rptr = RARRAY_CONST_PTR_TRANSIENT(ary) + rofs;
+ ary_memcpy0(ary, beg, rlen, rptr, target_ary);
+ }
+ ARY_SET_LEN(ary, len);
}
else {
- long alen;
-
- if (olen - len > ARY_MAX_SIZE - rlen) {
- rb_raise(rb_eIndexError, "index %ld too big", olen + rlen - len);
- }
- rb_ary_modify(ary);
- alen = olen + rlen - len;
- if (alen >= ARY_CAPA(ary)) {
- ary_double_capa(ary, alen);
- }
-
- if (len != rlen) {
- RARRAY_PTR_USE(ary, ptr,
- MEMMOVE(ptr + beg + rlen, ptr + beg + len,
- VALUE, olen - (beg + len)));
- ARY_SET_LEN(ary, alen);
- }
- if (rlen > 0) {
- if (rofs != -1) rptr = RARRAY_CONST_PTR(ary) + rofs;
- MEMMOVE(RARRAY_PTR(ary) + beg, rptr, VALUE, rlen);
- }
+ long alen;
+
+ if (olen - len > ARY_MAX_SIZE - rlen) {
+ rb_raise(rb_eIndexError, "index %ld too big", olen + rlen - len);
+ }
+ rb_ary_modify(ary);
+ alen = olen + rlen - len;
+ if (alen >= ARY_CAPA(ary)) {
+ ary_double_capa(ary, alen);
+ }
+
+ if (len != rlen) {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr,
+ MEMMOVE(ptr + beg + rlen, ptr + beg + len,
+ VALUE, olen - (beg + len)));
+ ARY_SET_LEN(ary, alen);
+ }
+ if (rlen > 0) {
+ if (rofs != -1) rptr = RARRAY_CONST_PTR_TRANSIENT(ary) + rofs;
+ /* give up wb-protected ary */
+ RB_OBJ_WB_UNPROTECT_FOR(ARRAY, ary);
+
+ /* do not use RARRAY_PTR() because it can causes GC.
+ * ary can contain T_NONE object because it is not cleared.
+ */
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr,
+ MEMMOVE(ptr + beg, rptr, VALUE, rlen));
+ }
}
}
@@ -1632,22 +2408,14 @@ rb_ary_set_len(VALUE ary, long len)
rb_ary_modify_check(ary);
if (ARY_SHARED_P(ary)) {
- rb_raise(rb_eRuntimeError, "can't set length of shared ");
+ rb_raise(rb_eRuntimeError, "can't set length of shared ");
}
if (len > (capa = (long)ARY_CAPA(ary))) {
- rb_bug("probable buffer overflow: %ld for %ld", len, capa);
+ rb_bug("probable buffer overflow: %ld for %ld", len, capa);
}
ARY_SET_LEN(ary, len);
}
-/*!
- * expands or shrinks \a ary to \a len elements.
- * expanded region will be filled with Qnil.
- * \param ary an array
- * \param len new size
- * \return \a ary
- * \post the size of \a ary is \a len.
- */
VALUE
rb_ary_resize(VALUE ary, long len)
{
@@ -1657,117 +2425,231 @@ rb_ary_resize(VALUE ary, long len)
olen = RARRAY_LEN(ary);
if (len == olen) return ary;
if (len > ARY_MAX_SIZE) {
- rb_raise(rb_eIndexError, "index %ld too big", len);
+ rb_raise(rb_eIndexError, "index %ld too big", len);
}
if (len > olen) {
- if (len >= ARY_CAPA(ary)) {
- ary_double_capa(ary, len);
- }
- ary_mem_clear(ary, olen, len - olen);
- ARY_SET_LEN(ary, len);
+ if (len >= ARY_CAPA(ary)) {
+ ary_double_capa(ary, len);
+ }
+ ary_mem_clear(ary, olen, len - olen);
+ ARY_SET_LEN(ary, len);
}
else if (ARY_EMBED_P(ary)) {
ARY_SET_EMBED_LEN(ary, len);
}
- else if (len <= RARRAY_EMBED_LEN_MAX) {
- VALUE tmp[RARRAY_EMBED_LEN_MAX];
- MEMCPY(tmp, ARY_HEAP_PTR(ary), VALUE, len);
- ary_discard(ary);
- MEMCPY((VALUE *)ARY_EMBED_PTR(ary), tmp, VALUE, len); /* WB: no new reference */
+ else if (len <= ary_embed_capa(ary)) {
+ const VALUE *ptr = ARY_HEAP_PTR(ary);
+ long ptr_capa = ARY_HEAP_SIZE(ary);
+ bool is_malloc_ptr = !ARY_SHARED_P(ary) && !RARRAY_TRANSIENT_P(ary);
+
+ FL_UNSET(ary, RARRAY_TRANSIENT_FLAG);
+ FL_SET_EMBED(ary);
+
+ MEMCPY((VALUE *)ARY_EMBED_PTR(ary), ptr, VALUE, len); /* WB: no new reference */
ARY_SET_EMBED_LEN(ary, len);
+
+ if (is_malloc_ptr) ruby_sized_xfree((void *)ptr, ptr_capa);
}
else {
- if (olen > len + ARY_DEFAULT_SIZE) {
- SIZED_REALLOC_N(RARRAY(ary)->as.heap.ptr, VALUE, len, RARRAY(ary)->as.heap.aux.capa);
- ARY_SET_CAPA(ary, len);
- }
- ARY_SET_HEAP_LEN(ary, len);
+ if (olen > len + ARY_DEFAULT_SIZE) {
+ size_t new_capa = ary_heap_realloc(ary, len);
+ ARY_SET_CAPA(ary, new_capa);
+ }
+ ARY_SET_HEAP_LEN(ary, len);
}
+ ary_verify(ary);
return ary;
}
+static VALUE
+ary_aset_by_rb_ary_store(VALUE ary, long key, VALUE val)
+{
+ rb_ary_store(ary, key, val);
+ return val;
+}
+
+static VALUE
+ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val)
+{
+ VALUE rpl = rb_ary_to_ary(val);
+ rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR_TRANSIENT(rpl), RARRAY_LEN(rpl));
+ RB_GC_GUARD(rpl);
+ return val;
+}
+
/*
* call-seq:
- * ary[index] = obj -> obj
- * ary[start, length] = obj or other_ary or nil -> obj or other_ary or nil
- * ary[range] = obj or other_ary or nil -> obj or other_ary or nil
- *
- * Element Assignment --- Sets the element at +index+, or replaces a subarray
- * from the +start+ index for +length+ elements, or replaces a subarray
- * specified by the +range+ of indices.
- *
- * If indices are greater than the current capacity of the array, the array
- * grows automatically. Elements are inserted into the array at +start+ if
- * +length+ is zero.
- *
- * Negative indices will count backward from the end of the array. For
- * +start+ and +range+ cases the starting index is just before an element.
- *
- * An IndexError is raised if a negative index points past the beginning of
- * the array.
- *
- * See also Array#push, and Array#unshift.
- *
- * a = Array.new
- * a[4] = "4"; #=> [nil, nil, nil, nil, "4"]
- * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"]
- * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"]
- * a[0, 2] = "?" #=> ["?", 2, nil, "4"]
- * a[0..2] = "A" #=> ["A", "4"]
- * a[-1] = "Z" #=> ["A", "Z"]
- * a[1..-1] = nil #=> ["A", nil]
- * a[1..-1] = [] #=> ["A"]
- * a[0, 0] = [ 1, 2 ] #=> [1, 2, "A"]
- * a[3, 0] = "B" #=> [1, 2, "A", "B"]
+ * array[index] = object -> object
+ * array[start, length] = object -> object
+ * array[range] = object -> object
+ *
+ * Assigns elements in +self+; returns the given +object+.
+ *
+ * When \Integer argument +index+ is given, assigns +object+ to an element in +self+.
+ *
+ * If +index+ is non-negative, assigns +object+ the element at offset +index+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0] = 'foo' # => "foo"
+ * a # => ["foo", "bar", 2]
+ *
+ * If +index+ is greater than <tt>self.length</tt>, extends the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[7] = 'foo' # => "foo"
+ * a # => [:foo, "bar", 2, nil, nil, nil, nil, "foo"]
+ *
+ * If +index+ is negative, counts backwards from the end of the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[-1] = 'two' # => "two"
+ * a # => [:foo, "bar", "two"]
+ *
+ * When \Integer arguments +start+ and +length+ are given and +object+ is not an \Array,
+ * removes <tt>length - 1</tt> elements beginning at offset +start+,
+ * and assigns +object+ at offset +start+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0, 2] = 'foo' # => "foo"
+ * a # => ["foo", 2]
+ *
+ * If +start+ is negative, counts backwards from the end of the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[-2, 2] = 'foo' # => "foo"
+ * a # => [:foo, "foo"]
+ *
+ * If +start+ is non-negative and outside the array (<tt> >= self.size</tt>),
+ * extends the array with +nil+, assigns +object+ at offset +start+,
+ * and ignores +length+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[6, 50] = 'foo' # => "foo"
+ * a # => [:foo, "bar", 2, nil, nil, nil, "foo"]
+ *
+ * If +length+ is zero, shifts elements at and following offset +start+
+ * and assigns +object+ at offset +start+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[1, 0] = 'foo' # => "foo"
+ * a # => [:foo, "foo", "bar", 2]
+ *
+ * If +length+ is too large for the existing array, does not extend the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[1, 5] = 'foo' # => "foo"
+ * a # => [:foo, "foo"]
+ *
+ * When \Range argument +range+ is given and +object+ is an \Array,
+ * removes <tt>length - 1</tt> elements beginning at offset +start+,
+ * and assigns +object+ at offset +start+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[0..1] = 'foo' # => "foo"
+ * a # => ["foo", 2]
+ *
+ * if <tt>range.begin</tt> is negative, counts backwards from the end of the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[-2..2] = 'foo' # => "foo"
+ * a # => [:foo, "foo"]
+ *
+ * If the array length is less than <tt>range.begin</tt>,
+ * assigns +object+ at offset <tt>range.begin</tt>, and ignores +length+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[6..50] = 'foo' # => "foo"
+ * a # => [:foo, "bar", 2, nil, nil, nil, "foo"]
+ *
+ * If <tt>range.end</tt> is zero, shifts elements at and following offset +start+
+ * and assigns +object+ at offset +start+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[1..0] = 'foo' # => "foo"
+ * a # => [:foo, "foo", "bar", 2]
+ *
+ * If <tt>range.end</tt> is negative, assigns +object+ at offset +start+,
+ * retains <tt>range.end.abs -1</tt> elements past that, and removes those beyond:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[1..-1] = 'foo' # => "foo"
+ * a # => [:foo, "foo"]
+ * a = [:foo, 'bar', 2]
+ * a[1..-2] = 'foo' # => "foo"
+ * a # => [:foo, "foo", 2]
+ * a = [:foo, 'bar', 2]
+ * a[1..-3] = 'foo' # => "foo"
+ * a # => [:foo, "foo", "bar", 2]
+ * a = [:foo, 'bar', 2]
+ *
+ * If <tt>range.end</tt> is too large for the existing array,
+ * replaces array elements, but does not extend the array with +nil+ values:
+ *
+ * a = [:foo, 'bar', 2]
+ * a[1..5] = 'foo' # => "foo"
+ * a # => [:foo, "foo"]
+ *
*/
static VALUE
rb_ary_aset(int argc, VALUE *argv, VALUE ary)
{
long offset, beg, len;
- VALUE rpl;
+ rb_check_arity(argc, 2, 3);
+ rb_ary_modify_check(ary);
if (argc == 3) {
- rb_ary_modify_check(ary);
- beg = NUM2LONG(argv[0]);
- len = NUM2LONG(argv[1]);
- goto range;
+ beg = NUM2LONG(argv[0]);
+ len = NUM2LONG(argv[1]);
+ return ary_aset_by_rb_ary_splice(ary, beg, len, argv[2]);
}
- rb_check_arity(argc, 2, 2);
- rb_ary_modify_check(ary);
if (FIXNUM_P(argv[0])) {
- offset = FIX2LONG(argv[0]);
- goto fixnum;
+ offset = FIX2LONG(argv[0]);
+ return ary_aset_by_rb_ary_store(ary, offset, argv[1]);
}
if (rb_range_beg_len(argv[0], &beg, &len, RARRAY_LEN(ary), 1)) {
- /* check if idx is Range */
- range:
- rpl = rb_ary_to_ary(argv[argc-1]);
- rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR(rpl), RARRAY_LEN(rpl));
- RB_GC_GUARD(rpl);
- return argv[argc-1];
+ /* check if idx is Range */
+ return ary_aset_by_rb_ary_splice(ary, beg, len, argv[1]);
}
offset = NUM2LONG(argv[0]);
-fixnum:
- rb_ary_store(ary, offset, argv[1]);
- return argv[1];
+ return ary_aset_by_rb_ary_store(ary, offset, argv[1]);
}
/*
* call-seq:
- * ary.insert(index, obj...) -> ary
+ * array.insert(index, *objects) -> self
+ *
+ * Inserts given +objects+ before or after the element at \Integer index +offset+;
+ * returns +self+.
+ *
+ * When +index+ is non-negative, inserts all given +objects+
+ * before the element at offset +index+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.insert(1, :bat, :bam) # => [:foo, :bat, :bam, "bar", 2]
*
- * Inserts the given values before the element with the given +index+.
+ * Extends the array if +index+ is beyond the array (<tt>index >= self.size</tt>):
*
- * Negative indices count backwards from the end of the array, where +-1+ is
- * the last element. If a negative index is used, the given values will be
- * inserted after that element, so using an index of +-1+ will insert the
- * values at the end of the array.
+ * a = [:foo, 'bar', 2]
+ * a.insert(5, :bat, :bam)
+ * a # => [:foo, "bar", 2, nil, nil, :bat, :bam]
+ *
+ * Does nothing if no objects given:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.insert(1)
+ * a.insert(50)
+ * a.insert(-50)
+ * a # => [:foo, "bar", 2]
+ *
+ * When +index+ is negative, inserts all given +objects+
+ * _after_ the element at offset <tt>index+self.size</tt>:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.insert(-2, :bat, :bam)
+ * a # => [:foo, "bar", :bat, :bam, 2]
*
- * a = %w{ a b c d }
- * a.insert(2, 99) #=> ["a", "b", 99, "c", "d"]
- * a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"]
*/
static VALUE
@@ -1780,15 +2662,15 @@ rb_ary_insert(int argc, VALUE *argv, VALUE ary)
pos = NUM2LONG(argv[0]);
if (argc == 1) return ary;
if (pos == -1) {
- pos = RARRAY_LEN(ary);
+ pos = RARRAY_LEN(ary);
}
else if (pos < 0) {
- long minpos = -RARRAY_LEN(ary) - 1;
- if (pos < minpos) {
- rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
- pos, minpos);
- }
- pos++;
+ long minpos = -RARRAY_LEN(ary) - 1;
+ if (pos < minpos) {
+ rb_raise(rb_eIndexError, "index %ld too small for array; minimum: %ld",
+ pos, minpos);
+ }
+ pos++;
}
rb_ary_splice(ary, pos, 0, argv + 1, argc - 1);
return ary;
@@ -1805,50 +2687,104 @@ ary_enum_length(VALUE ary, VALUE args, VALUE eobj)
/*
* call-seq:
- * ary.each {|item| block} -> ary
- * ary.each -> Enumerator
+ * array.each {|element| ... } -> self
+ * array.each -> Enumerator
+ *
+ * Iterates over array elements.
+ *
+ * When a block given, passes each successive array element to the block;
+ * returns +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.each {|element| puts "#{element.class} #{element}" }
+ *
+ * Output:
+ *
+ * Symbol foo
+ * String bar
+ * Integer 2
+ *
+ * Allows the array to be modified during iteration:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.each {|element| puts element; a.clear if element.to_s.start_with?('b') }
+ *
+ * Output:
*
- * Calls the given block once for each element in +self+, passing that element
- * as a parameter. Returns the array itself.
+ * foo
+ * bar
*
- * If no block is given, an Enumerator is returned.
+ * When no block given, returns a new \Enumerator:
+ * a = [:foo, 'bar', 2]
*
- * a = [ "a", "b", "c" ]
- * a.each {|x| print x, " -- " }
+ * e = a.each
+ * e # => #<Enumerator: [:foo, "bar", 2]:each>
+ * a1 = e.each {|element| puts "#{element.class} #{element}" }
*
- * produces:
+ * Output:
*
- * a -- b -- c --
+ * Symbol foo
+ * String bar
+ * Integer 2
+ *
+ * Related: #each_index, #reverse_each.
*/
VALUE
rb_ary_each(VALUE ary)
{
long i;
-
+ ary_verify(ary);
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
- rb_yield(RARRAY_AREF(ary, i));
+ rb_yield(RARRAY_AREF(ary, i));
}
return ary;
}
/*
* call-seq:
- * ary.each_index {|index| block} -> ary
- * ary.each_index -> Enumerator
+ * array.each_index {|index| ... } -> self
+ * array.each_index -> Enumerator
+ *
+ * Iterates over array indexes.
+ *
+ * When a block given, passes each successive array index to the block;
+ * returns +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.each_index {|index| puts "#{index} #{a[index]}" }
+ *
+ * Output:
*
- * Same as Array#each, but passes the +index+ of the element instead of the
- * element itself.
+ * 0 foo
+ * 1 bar
+ * 2 2
*
- * An Enumerator is returned if no block is given.
+ * Allows the array to be modified during iteration:
*
- * a = [ "a", "b", "c" ]
- * a.each_index {|x| print x, " -- " }
+ * a = [:foo, 'bar', 2]
+ * a.each_index {|index| puts index; a.clear if index > 0 }
*
- * produces:
+ * Output:
*
- * 0 -- 1 -- 2 --
+ * 0
+ * 1
+ *
+ * When no block given, returns a new \Enumerator:
+ *
+ * a = [:foo, 'bar', 2]
+ * e = a.each_index
+ * e # => #<Enumerator: [:foo, "bar", 2]:each_index>
+ * a1 = e.each {|index| puts "#{index} #{a[index]}"}
+ *
+ * Output:
+ *
+ * 0 foo
+ * 1 bar
+ * 2 2
+ *
+ * Related: #each, #reverse_each.
*/
static VALUE
@@ -1858,24 +2794,54 @@ rb_ary_each_index(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
for (i=0; i<RARRAY_LEN(ary); i++) {
- rb_yield(LONG2NUM(i));
+ rb_yield(LONG2NUM(i));
}
return ary;
}
/*
* call-seq:
- * ary.reverse_each {|item| block} -> ary
- * ary.reverse_each -> Enumerator
+ * array.reverse_each {|element| ... } -> self
+ * array.reverse_each -> Enumerator
+ *
+ * Iterates backwards over array elements.
*
- * Same as Array#each, but traverses +self+ in reverse order.
+ * When a block given, passes, in reverse order, each element to the block;
+ * returns +self+:
*
- * a = [ "a", "b", "c" ]
- * a.reverse_each {|x| print x, " " }
+ * a = [:foo, 'bar', 2]
+ * a.reverse_each {|element| puts "#{element.class} #{element}" }
*
- * produces:
+ * Output:
*
- * c b a
+ * Integer 2
+ * String bar
+ * Symbol foo
+ *
+ * Allows the array to be modified during iteration:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.reverse_each {|element| puts element; a.clear if element.to_s.start_with?('b') }
+ *
+ * Output:
+ *
+ * 2
+ * bar
+ *
+ * When no block given, returns a new \Enumerator:
+ *
+ * a = [:foo, 'bar', 2]
+ * e = a.reverse_each
+ * e # => #<Enumerator: [:foo, "bar", 2]:reverse_each>
+ * a1 = e.each {|element| puts "#{element.class} #{element}" }
+ *
+ * Output:
+ *
+ * Integer 2
+ * String bar
+ * Symbol foo
+ *
+ * Related: #each, #each_index.
*/
static VALUE
@@ -1886,24 +2852,21 @@ rb_ary_reverse_each(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
len = RARRAY_LEN(ary);
while (len--) {
- long nlen;
- rb_yield(RARRAY_AREF(ary, len));
- nlen = RARRAY_LEN(ary);
- if (nlen < len) {
- len = nlen;
- }
+ long nlen;
+ rb_yield(RARRAY_AREF(ary, len));
+ nlen = RARRAY_LEN(ary);
+ if (nlen < len) {
+ len = nlen;
+ }
}
return ary;
}
/*
* call-seq:
- * ary.length -> int
+ * array.length -> an_integer
*
- * Returns the number of elements in +self+. May be zero.
- *
- * [ 1, 2, 3, 4, 5 ].length #=> 5
- * [].length #=> 0
+ * Returns the count of elements in +self+.
*/
static VALUE
@@ -1915,19 +2878,16 @@ rb_ary_length(VALUE ary)
/*
* call-seq:
- * ary.empty? -> true or false
- *
- * Returns +true+ if +self+ contains no elements.
+ * array.empty? -> true or false
*
- * [].empty? #=> true
+ * Returns +true+ if the count of elements in +self+ is zero,
+ * +false+ otherwise.
*/
static VALUE
rb_ary_empty_p(VALUE ary)
{
- if (RARRAY_LEN(ary) == 0)
- return Qtrue;
- return Qfalse;
+ return RBOOL(RARRAY_LEN(ary) == 0);
}
VALUE
@@ -1935,15 +2895,18 @@ rb_ary_dup(VALUE ary)
{
long len = RARRAY_LEN(ary);
VALUE dup = rb_ary_new2(len);
- ary_memcpy(dup, 0, len, RARRAY_CONST_PTR(ary));
+ ary_memcpy(dup, 0, len, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_LEN(dup, len);
+
+ ary_verify(ary);
+ ary_verify(dup);
return dup;
}
VALUE
rb_ary_resurrect(VALUE ary)
{
- return rb_ary_new4(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary));
+ return ary_make_partial(ary, rb_cArray, 0, RARRAY_LEN(ary));
}
extern VALUE rb_output_fs;
@@ -1960,15 +2923,15 @@ recursive_join(VALUE obj, VALUE argp, int recur)
int *first = (int *)arg[3];
if (recur) {
- rb_raise(rb_eArgError, "recursive array join");
+ rb_raise(rb_eArgError, "recursive array join");
}
else {
- ary_join_1(obj, ary, sep, 0, result, first);
+ ary_join_1(obj, ary, sep, 0, result, first);
}
return Qnil;
}
-static void
+static long
ary_join_0(VALUE ary, VALUE sep, long max, VALUE result)
{
long i;
@@ -1976,11 +2939,40 @@ ary_join_0(VALUE ary, VALUE sep, long max, VALUE result)
if (max > 0) rb_enc_copy(result, RARRAY_AREF(ary, 0));
for (i=0; i<max; i++) {
- val = RARRAY_AREF(ary, i);
- if (i > 0 && !NIL_P(sep))
- rb_str_buf_append(result, sep);
- rb_str_buf_append(result, val);
- if (OBJ_TAINTED(val)) OBJ_TAINT(result);
+ val = RARRAY_AREF(ary, i);
+ if (!RB_TYPE_P(val, T_STRING)) break;
+ if (i > 0 && !NIL_P(sep))
+ rb_str_buf_append(result, sep);
+ rb_str_buf_append(result, val);
+ }
+ return i;
+}
+
+static void
+ary_join_1_str(VALUE dst, VALUE src, int *first)
+{
+ rb_str_buf_append(dst, src);
+ if (*first) {
+ rb_enc_copy(dst, src);
+ *first = FALSE;
+ }
+}
+
+static void
+ary_join_1_ary(VALUE obj, VALUE ary, VALUE sep, VALUE result, VALUE val, int *first)
+{
+ if (val == ary) {
+ rb_raise(rb_eArgError, "recursive array join");
+ }
+ else {
+ VALUE args[4];
+
+ *first = FALSE;
+ args[0] = val;
+ args[1] = sep;
+ args[2] = result;
+ args[3] = (VALUE)first;
+ rb_exec_recursive(recursive_join, obj, (VALUE)args);
}
}
@@ -1990,50 +2982,25 @@ ary_join_1(VALUE obj, VALUE ary, VALUE sep, long i, VALUE result, int *first)
VALUE val, tmp;
for (; i<RARRAY_LEN(ary); i++) {
- if (i > 0 && !NIL_P(sep))
- rb_str_buf_append(result, sep);
-
- val = RARRAY_AREF(ary, i);
- if (RB_TYPE_P(val, T_STRING)) {
- str_join:
- rb_str_buf_append(result, val);
- if (*first) {
- rb_enc_copy(result, val);
- *first = FALSE;
- }
- }
- else if (RB_TYPE_P(val, T_ARRAY)) {
- obj = val;
- ary_join:
- if (val == ary) {
- rb_raise(rb_eArgError, "recursive array join");
- }
- else {
- VALUE args[4];
-
- *first = FALSE;
- args[0] = val;
- args[1] = sep;
- args[2] = result;
- args[3] = (VALUE)first;
- rb_exec_recursive(recursive_join, obj, (VALUE)args);
- }
- }
- else {
- tmp = rb_check_string_type(val);
- if (!NIL_P(tmp)) {
- val = tmp;
- goto str_join;
- }
- tmp = rb_check_array_type(val);
- if (!NIL_P(tmp)) {
- obj = val;
- val = tmp;
- goto ary_join;
- }
- val = rb_obj_as_string(val);
- goto str_join;
- }
+ if (i > 0 && !NIL_P(sep))
+ rb_str_buf_append(result, sep);
+
+ val = RARRAY_AREF(ary, i);
+ if (RB_TYPE_P(val, T_STRING)) {
+ ary_join_1_str(result, val, first);
+ }
+ else if (RB_TYPE_P(val, T_ARRAY)) {
+ ary_join_1_ary(val, ary, sep, result, val, first);
+ }
+ else if (!NIL_P(tmp = rb_check_string_type(val))) {
+ ary_join_1_str(result, tmp, first);
+ }
+ else if (!NIL_P(tmp = rb_check_array_type(val))) {
+ ary_join_1_ary(val, ary, sep, result, tmp, first);
+ }
+ else {
+ ary_join_1_str(result, rb_obj_as_string(val), first);
+ }
}
}
@@ -2041,36 +3008,36 @@ VALUE
rb_ary_join(VALUE ary, VALUE sep)
{
long len = 1, i;
- int taint = FALSE;
VALUE val, tmp, result;
if (RARRAY_LEN(ary) == 0) return rb_usascii_str_new(0, 0);
- if (OBJ_TAINTED(ary)) taint = TRUE;
if (!NIL_P(sep)) {
- StringValue(sep);
- len += RSTRING_LEN(sep) * (RARRAY_LEN(ary) - 1);
+ StringValue(sep);
+ len += RSTRING_LEN(sep) * (RARRAY_LEN(ary) - 1);
}
for (i=0; i<RARRAY_LEN(ary); i++) {
- val = RARRAY_AREF(ary, i);
- tmp = rb_check_string_type(val);
-
- if (NIL_P(tmp) || tmp != val) {
- int first;
- result = rb_str_buf_new(len + (RARRAY_LEN(ary)-i)*10);
- rb_enc_associate(result, rb_usascii_encoding());
- if (taint) OBJ_TAINT(result);
- ary_join_0(ary, sep, i, result);
- first = i == 0;
- ary_join_1(ary, ary, sep, i, result, &first);
- return result;
- }
+ val = RARRAY_AREF(ary, i);
+ tmp = rb_check_string_type(val);
+
+ if (NIL_P(tmp) || tmp != val) {
+ int first;
+ long n = RARRAY_LEN(ary);
+ if (i > n) i = n;
+ result = rb_str_buf_new(len + (n-i)*10);
+ rb_enc_associate(result, rb_usascii_encoding());
+ i = ary_join_0(ary, sep, i, result);
+ first = i == 0;
+ ary_join_1(ary, ary, sep, i, result, &first);
+ return result;
+ }
- len += RSTRING_LEN(tmp);
+ len += RSTRING_LEN(tmp);
}
- result = rb_str_buf_new(len);
- if (taint) OBJ_TAINT(result);
+ result = rb_str_new(0, len);
+ rb_str_set_len(result, 0);
+
ary_join_0(ary, sep, RARRAY_LEN(ary), result);
return result;
@@ -2078,29 +3045,43 @@ rb_ary_join(VALUE ary, VALUE sep)
/*
* call-seq:
- * ary.join(separator=$,) -> str
+ * array.join ->new_string
+ * array.join(separator = $,) -> new_string
*
- * Returns a string created by converting each element of the array to
- * a string, separated by the given +separator+.
- * If the +separator+ is +nil+, it uses current <code>$,</code>.
- * If both the +separator+ and <code>$,</code> are +nil+,
- * it uses an empty string.
+ * Returns the new \String formed by joining the array elements after conversion.
+ * For each element +element+:
*
- * [ "a", "b", "c" ].join #=> "abc"
- * [ "a", "b", "c" ].join("-") #=> "a-b-c"
+ * - Uses <tt>element.to_s</tt> if +element+ is not a <tt>kind_of?(Array)</tt>.
+ * - Uses recursive <tt>element.join(separator)</tt> if +element+ is a <tt>kind_of?(Array)</tt>.
*
- * For nested arrays, join is applied recursively:
+ * With no argument, joins using the output field separator, <tt>$,</tt>:
+ *
+ * a = [:foo, 'bar', 2]
+ * $, # => nil
+ * a.join # => "foobar2"
+ *
+ * With \string argument +separator+, joins using that separator:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.join("\n") # => "foo\nbar\n2"
+ *
+ * Joins recursively for nested Arrays:
+ *
+ * a = [:foo, [:bar, [:baz, :bat]]]
+ * a.join # => "foobarbazbat"
*
- * [ "a", [1, 2, [:x, :y]], "b" ].join("-") #=> "a-1-2-x-y-b"
*/
-
static VALUE
rb_ary_join_m(int argc, VALUE *argv, VALUE ary)
{
VALUE sep;
- rb_scan_args(argc, argv, "01", &sep);
- if (NIL_P(sep)) sep = rb_output_fs;
+ if (rb_check_arity(argc, 0, 1) == 0 || NIL_P(sep = argv[0])) {
+ sep = rb_output_fs;
+ if (!NIL_P(sep)) {
+ rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$, is set to non-nil value");
+ }
+ }
return rb_ary_join(ary, sep);
}
@@ -2108,32 +3089,32 @@ rb_ary_join_m(int argc, VALUE *argv, VALUE ary)
static VALUE
inspect_ary(VALUE ary, VALUE dummy, int recur)
{
- int tainted = OBJ_TAINTED(ary);
long i;
VALUE s, str;
if (recur) return rb_usascii_str_new_cstr("[...]");
str = rb_str_buf_new2("[");
for (i=0; i<RARRAY_LEN(ary); i++) {
- s = rb_inspect(RARRAY_AREF(ary, i));
- if (OBJ_TAINTED(s)) tainted = TRUE;
- if (i > 0) rb_str_buf_cat2(str, ", ");
- else rb_enc_copy(str, s);
- rb_str_buf_append(str, s);
+ s = rb_inspect(RARRAY_AREF(ary, i));
+ if (i > 0) rb_str_buf_cat2(str, ", ");
+ else rb_enc_copy(str, s);
+ rb_str_buf_append(str, s);
}
rb_str_buf_cat2(str, "]");
- if (tainted) OBJ_TAINT(str);
return str;
}
/*
* call-seq:
- * ary.inspect -> string
- * ary.to_s -> string
+ * array.inspect -> new_string
+ *
+ * Returns the new \String formed by calling method <tt>#inspect</tt>
+ * on each array element:
*
- * Creates a string representation of +self+.
+ * a = [:foo, 'bar', 2]
+ * a.inspect # => "[:foo, \"bar\", 2]"
*
- * [ "a", "b", "c" ].to_s #=> "[\"a\", \"b\", \"c\"]"
+ * Array#to_s is an alias for Array#inspect.
*/
static VALUE
@@ -2151,33 +3132,59 @@ rb_ary_to_s(VALUE ary)
/*
* call-seq:
- * ary.to_a -> ary
+ * to_a -> self or new_array
*
- * Returns +self+.
+ * When +self+ is an instance of \Array, returns +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.to_a # => [:foo, "bar", 2]
+ *
+ * Otherwise, returns a new \Array containing the elements of +self+:
+ *
+ * class MyArray < Array; end
+ * a = MyArray.new(['foo', 'bar', 'two'])
+ * a.instance_of?(Array) # => false
+ * a.kind_of?(Array) # => true
+ * a1 = a.to_a
+ * a1 # => ["foo", "bar", "two"]
+ * a1.class # => Array # Not MyArray
*
- * If called on a subclass of Array, converts the receiver to an Array object.
*/
static VALUE
rb_ary_to_a(VALUE ary)
{
if (rb_obj_class(ary) != rb_cArray) {
- VALUE dup = rb_ary_new2(RARRAY_LEN(ary));
- rb_ary_replace(dup, ary);
- return dup;
+ VALUE dup = rb_ary_new2(RARRAY_LEN(ary));
+ rb_ary_replace(dup, ary);
+ return dup;
}
return ary;
}
/*
* call-seq:
- * ary.to_h -> hash
+ * array.to_h -> new_hash
+ * array.to_h {|item| ... } -> new_hash
+ *
+ * Returns a new \Hash formed from +self+.
+ *
+ * When a block is given, calls the block with each array element;
+ * the block must return a 2-element \Array whose two elements
+ * form a key-value pair in the returned \Hash:
+ *
+ * a = ['foo', :bar, 1, [2, 3], {baz: 4}]
+ * h = a.to_h {|item| [item, item] }
+ * h # => {"foo"=>"foo", :bar=>:bar, 1=>1, [2, 3]=>[2, 3], {:baz=>4}=>{:baz=>4}}
*
- * Returns the result of interpreting <i>ary</i> as an array of
- * <tt>[key, value]</tt> pairs.
+ * When no block is given, +self+ must be an \Array of 2-element sub-arrays,
+ * each sub-array is formed into a key-value pair in the new \Hash:
+ *
+ * [].to_h # => {}
+ * a = [['foo', 'zero'], ['bar', 'one'], ['baz', 'two']]
+ * h = a.to_h
+ * h # => {"foo"=>"zero", "bar"=>"one", "baz"=>"two"}
*
- * [[:foo, :bar], [1, 2]].to_h
- * # => {:foo => :bar, 1 => 2}
*/
static VALUE
@@ -2185,25 +3192,28 @@ rb_ary_to_h(VALUE ary)
{
long i;
VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary));
+ int block_given = rb_block_given_p();
+
for (i=0; i<RARRAY_LEN(ary); i++) {
- const VALUE elt = rb_ary_elt(ary, i);
- const VALUE key_value_pair = rb_check_array_type(elt);
- if (NIL_P(key_value_pair)) {
- rb_raise(rb_eTypeError, "wrong element type %"PRIsVALUE" at %ld (expected array)",
- rb_obj_class(elt), i);
- }
- if (RARRAY_LEN(key_value_pair) != 2) {
- rb_raise(rb_eArgError, "wrong array length at %ld (expected 2, was %ld)",
- i, RARRAY_LEN(key_value_pair));
- }
- rb_hash_aset(hash, RARRAY_AREF(key_value_pair, 0), RARRAY_AREF(key_value_pair, 1));
+ const VALUE e = rb_ary_elt(ary, i);
+ const VALUE elt = block_given ? rb_yield_force_blockarg(e) : e;
+ const VALUE key_value_pair = rb_check_array_type(elt);
+ if (NIL_P(key_value_pair)) {
+ rb_raise(rb_eTypeError, "wrong element type %"PRIsVALUE" at %ld (expected array)",
+ rb_obj_class(elt), i);
+ }
+ if (RARRAY_LEN(key_value_pair) != 2) {
+ rb_raise(rb_eArgError, "wrong array length at %ld (expected 2, was %ld)",
+ i, RARRAY_LEN(key_value_pair));
+ }
+ rb_hash_aset(hash, RARRAY_AREF(key_value_pair, 0), RARRAY_AREF(key_value_pair, 1));
}
return hash;
}
/*
* call-seq:
- * ary.to_ary -> ary
+ * array.to_ary -> self
*
* Returns +self+.
*/
@@ -2218,9 +3228,9 @@ static void
ary_reverse(VALUE *p1, VALUE *p2)
{
while (p1 < p2) {
- VALUE tmp = *p1;
- *p1++ = *p2;
- *p2-- = tmp;
+ VALUE tmp = *p1;
+ *p1++ = *p2;
+ *p2-- = tmp;
}
}
@@ -2232,23 +3242,23 @@ rb_ary_reverse(VALUE ary)
rb_ary_modify(ary);
if (len > 1) {
- RARRAY_PTR_USE(ary, p1, {
- p2 = p1 + len - 1; /* points last item */
- ary_reverse(p1, p2);
- }); /* WB: no new reference */
+ RARRAY_PTR_USE_TRANSIENT(ary, p1, {
+ p2 = p1 + len - 1; /* points last item */
+ ary_reverse(p1, p2);
+ }); /* WB: no new reference */
}
return ary;
}
/*
* call-seq:
- * ary.reverse! -> ary
+ * array.reverse! -> self
+ *
+ * Reverses +self+ in place:
*
- * Reverses +self+ in place.
+ * a = ['foo', 'bar', 'two']
+ * a.reverse! # => ["two", "bar", "foo"]
*
- * a = [ "a", "b", "c" ]
- * a.reverse! #=> ["c", "b", "a"]
- * a #=> ["c", "b", "a"]
*/
static VALUE
@@ -2259,12 +3269,14 @@ rb_ary_reverse_bang(VALUE ary)
/*
* call-seq:
- * ary.reverse -> new_ary
+ * array.reverse -> new_array
+ *
+ * Returns a new \Array with the elements of +self+ in reverse order:
*
- * Returns a new array containing +self+'s elements in reverse order.
+ * a = ['foo', 'bar', 'two']
+ * a1 = a.reverse
+ * a1 # => ["two", "bar", "foo"]
*
- * [ "a", "b", "c" ].reverse #=> ["c", "b", "a"]
- * [ 1 ].reverse #=> [1]
*/
static VALUE
@@ -2274,9 +3286,9 @@ rb_ary_reverse_m(VALUE ary)
VALUE dup = rb_ary_new2(len);
if (len > 0) {
- const VALUE *p1 = RARRAY_CONST_PTR(ary);
- VALUE *p2 = (VALUE *)RARRAY_CONST_PTR(dup) + len - 1;
- do *p2-- = *p1++; while (--len > 0);
+ const VALUE *p1 = RARRAY_CONST_PTR_TRANSIENT(ary);
+ VALUE *p2 = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(dup) + len - 1;
+ do *p2-- = *p1++; while (--len > 0);
}
ARY_SET_LEN(dup, RARRAY_LEN(ary));
return dup;
@@ -2288,73 +3300,143 @@ rotate_count(long cnt, long len)
return (cnt < 0) ? (len - (~cnt % len) - 1) : (cnt % len);
}
+static void
+ary_rotate_ptr(VALUE *ptr, long len, long cnt)
+{
+ if (cnt == 1) {
+ VALUE tmp = *ptr;
+ memmove(ptr, ptr + 1, sizeof(VALUE)*(len - 1));
+ *(ptr + len - 1) = tmp;
+ }
+ else if (cnt == len - 1) {
+ VALUE tmp = *(ptr + len - 1);
+ memmove(ptr + 1, ptr, sizeof(VALUE)*(len - 1));
+ *ptr = tmp;
+ }
+ else {
+ --len;
+ if (cnt < len) ary_reverse(ptr + cnt, ptr + len);
+ if (--cnt > 0) ary_reverse(ptr, ptr + cnt);
+ if (len > 0) ary_reverse(ptr, ptr + len);
+ }
+}
+
VALUE
rb_ary_rotate(VALUE ary, long cnt)
{
rb_ary_modify(ary);
if (cnt != 0) {
- VALUE *ptr = RARRAY_PTR(ary);
- long len = RARRAY_LEN(ary);
-
- if (len > 0 && (cnt = rotate_count(cnt, len)) > 0) {
- --len;
- if (cnt < len) ary_reverse(ptr + cnt, ptr + len);
- if (--cnt > 0) ary_reverse(ptr, ptr + cnt);
- if (len > 0) ary_reverse(ptr, ptr + len);
- return ary;
- }
+ long len = RARRAY_LEN(ary);
+ if (len > 1 && (cnt = rotate_count(cnt, len)) > 0) {
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, ary_rotate_ptr(ptr, len, cnt));
+ return ary;
+ }
}
-
return Qnil;
}
/*
* call-seq:
- * ary.rotate!(count=1) -> ary
+ * array.rotate! -> self
+ * array.rotate!(count) -> self
*
- * Rotates +self+ in place so that the element at +count+ comes first, and
- * returns +self+.
+ * Rotates +self+ in place by moving elements from one end to the other; returns +self+.
+ *
+ * When no argument given, rotates the first element to the last position:
+ *
+ * a = [:foo, 'bar', 2, 'bar']
+ * a.rotate! # => ["bar", 2, "bar", :foo]
+ *
+ * When given a non-negative \Integer +count+,
+ * rotates +count+ elements from the beginning to the end:
*
- * If +count+ is negative then it rotates in the opposite direction, starting
- * from the end of the array where +-1+ is the last element.
+ * a = [:foo, 'bar', 2]
+ * a.rotate!(2)
+ * a # => [2, :foo, "bar"]
+ *
+ * If +count+ is large, uses <tt>count % array.size</tt> as the count:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.rotate!(20)
+ * a # => [2, :foo, "bar"]
+ *
+ * If +count+ is zero, returns +self+ unmodified:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.rotate!(0)
+ * a # => [:foo, "bar", 2]
+ *
+ * When given a negative Integer +count+, rotates in the opposite direction,
+ * from end to beginning:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.rotate!(-2)
+ * a # => ["bar", 2, :foo]
+ *
+ * If +count+ is small (far from zero), uses <tt>count % array.size</tt> as the count:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.rotate!(-5)
+ * a # => ["bar", 2, :foo]
*
- * a = [ "a", "b", "c", "d" ]
- * a.rotate! #=> ["b", "c", "d", "a"]
- * a #=> ["b", "c", "d", "a"]
- * a.rotate!(2) #=> ["d", "a", "b", "c"]
- * a.rotate!(-3) #=> ["a", "b", "c", "d"]
*/
static VALUE
rb_ary_rotate_bang(int argc, VALUE *argv, VALUE ary)
{
- long n = 1;
-
- switch (argc) {
- case 1: n = NUM2LONG(argv[0]);
- case 0: break;
- default: rb_scan_args(argc, argv, "01", NULL);
- }
+ long n = (rb_check_arity(argc, 0, 1) ? NUM2LONG(argv[0]) : 1);
rb_ary_rotate(ary, n);
return ary;
}
/*
* call-seq:
- * ary.rotate(count=1) -> new_ary
+ * array.rotate -> new_array
+ * array.rotate(count) -> new_array
+ *
+ * Returns a new \Array formed from +self+ with elements
+ * rotated from one end to the other.
+ *
+ * When no argument given, returns a new \Array that is like +self+,
+ * except that the first element has been rotated to the last position:
*
- * Returns a new array by rotating +self+ so that the element at +count+ is
- * the first element of the new array.
+ * a = [:foo, 'bar', 2, 'bar']
+ * a1 = a.rotate
+ * a1 # => ["bar", 2, "bar", :foo]
*
- * If +count+ is negative then it rotates in the opposite direction, starting
- * from the end of +self+ where +-1+ is the last element.
+ * When given a non-negative \Integer +count+,
+ * returns a new \Array with +count+ elements rotated from the beginning to the end:
+ *
+ * a = [:foo, 'bar', 2]
+ * a1 = a.rotate(2)
+ * a1 # => [2, :foo, "bar"]
+ *
+ * If +count+ is large, uses <tt>count % array.size</tt> as the count:
+ *
+ * a = [:foo, 'bar', 2]
+ * a1 = a.rotate(20)
+ * a1 # => [2, :foo, "bar"]
+ *
+ * If +count+ is zero, returns a copy of +self+, unmodified:
+ *
+ * a = [:foo, 'bar', 2]
+ * a1 = a.rotate(0)
+ * a1 # => [:foo, "bar", 2]
+ *
+ * When given a negative \Integer +count+, rotates in the opposite direction,
+ * from end to beginning:
+ *
+ * a = [:foo, 'bar', 2]
+ * a1 = a.rotate(-2)
+ * a1 # => ["bar", 2, :foo]
+ *
+ * If +count+ is small (far from zero), uses <tt>count % array.size</tt> as the count:
+ *
+ * a = [:foo, 'bar', 2]
+ * a1 = a.rotate(-5)
+ * a1 # => ["bar", 2, :foo]
*
- * a = [ "a", "b", "c", "d" ]
- * a.rotate #=> ["b", "c", "d", "a"]
- * a #=> ["a", "b", "c", "d"]
- * a.rotate(2) #=> ["c", "d", "a", "b"]
- * a.rotate(-3) #=> ["b", "c", "d", "a"]
*/
static VALUE
@@ -2362,22 +3444,17 @@ rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary)
{
VALUE rotated;
const VALUE *ptr;
- long len, cnt = 1;
-
- switch (argc) {
- case 1: cnt = NUM2LONG(argv[0]);
- case 0: break;
- default: rb_scan_args(argc, argv, "01", NULL);
- }
+ long len;
+ long cnt = (rb_check_arity(argc, 0, 1) ? NUM2LONG(argv[0]) : 1);
len = RARRAY_LEN(ary);
rotated = rb_ary_new2(len);
if (len > 0) {
- cnt = rotate_count(cnt, len);
- ptr = RARRAY_CONST_PTR(ary);
- len -= cnt;
- ary_memcpy(rotated, 0, len, ptr + cnt);
- ary_memcpy(rotated, len, cnt, ptr);
+ cnt = rotate_count(cnt, len);
+ ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
+ len -= cnt;
+ ary_memcpy(rotated, 0, len, ptr + cnt);
+ ary_memcpy(rotated, len, cnt, ptr);
}
ARY_SET_LEN(rotated, RARRAY_LEN(ary));
return rotated;
@@ -2385,18 +3462,27 @@ rb_ary_rotate_m(int argc, VALUE *argv, VALUE ary)
struct ary_sort_data {
VALUE ary;
- struct cmp_opt_data cmp_opt;
+ VALUE receiver;
};
static VALUE
sort_reentered(VALUE ary)
{
if (RBASIC(ary)->klass) {
- rb_raise(rb_eRuntimeError, "sort reentered");
+ rb_raise(rb_eRuntimeError, "sort reentered");
}
return Qnil;
}
+static void
+sort_returned(struct ary_sort_data *data)
+{
+ if (rb_obj_frozen_p(data->receiver)) {
+ rb_raise(rb_eFrozenError, "array frozen during sort");
+ }
+ sort_reentered(data->ary);
+}
+
static int
sort_1(const void *ap, const void *bp, void *dummy)
{
@@ -2410,7 +3496,7 @@ sort_1(const void *ap, const void *bp, void *dummy)
args[1] = b;
retval = rb_yield_values2(2, args);
n = rb_cmpint(retval, a, b);
- sort_reentered(data->ary);
+ sort_returned(data);
return n;
}
@@ -2422,47 +3508,64 @@ sort_2(const void *ap, const void *bp, void *dummy)
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
int n;
- if (FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, Fixnum)) {
- if ((long)a > (long)b) return 1;
- if ((long)a < (long)b) return -1;
- return 0;
+ if (FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(INTEGER)) {
+ if ((long)a > (long)b) return 1;
+ if ((long)a < (long)b) return -1;
+ return 0;
}
- if (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data->cmp_opt, String)) {
- return rb_str_cmp(a, b);
+ if (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(STRING)) {
+ return rb_str_cmp(a, b);
}
- if (RB_FLOAT_TYPE_P(a) && CMP_OPTIMIZABLE(data->cmp_opt, Float)) {
- return rb_float_cmp(a, b);
+ if (RB_FLOAT_TYPE_P(a) && CMP_OPTIMIZABLE(FLOAT)) {
+ return rb_float_cmp(a, b);
}
retval = rb_funcallv(a, id_cmp, 1, &b);
n = rb_cmpint(retval, a, b);
- sort_reentered(data->ary);
+ sort_returned(data);
return n;
}
/*
* call-seq:
- * ary.sort! -> ary
- * ary.sort! {|a, b| block} -> ary
+ * array.sort! -> self
+ * array.sort! {|a, b| ... } -> self
+ *
+ * Returns +self+ with its elements sorted in place.
+ *
+ * With no block, compares elements using operator <tt><=></tt>
+ * (see Comparable):
+ *
+ * a = 'abcde'.split('').shuffle
+ * a # => ["e", "b", "d", "a", "c"]
+ * a.sort!
+ * a # => ["a", "b", "c", "d", "e"]
+ *
+ * With a block, calls the block with each element pair;
+ * for each element pair +a+ and +b+, the block should return an integer:
*
- * Sorts +self+ in place.
+ * - Negative when +b+ is to follow +a+.
+ * - Zero when +a+ and +b+ are equivalent.
+ * - Positive when +a+ is to follow +b+.
*
- * Comparisons for the sort will be done using the <code><=></code> operator
- * or using an optional code block.
+ * Example:
*
- * The block must implement a comparison between +a+ and +b+ and return
- * an integer less than 0 when +b+ follows +a+, +0+ when +a+ and +b+
- * are equivalent, or an integer greater than 0 when +a+ follows +b+.
+ * a = 'abcde'.split('').shuffle
+ * a # => ["e", "b", "d", "a", "c"]
+ * a.sort! {|a, b| a <=> b }
+ * a # => ["a", "b", "c", "d", "e"]
+ * a.sort! {|a, b| b <=> a }
+ * a # => ["e", "d", "c", "b", "a"]
*
- * The result is not guaranteed to be stable. When the comparison of two
- * elements returns +0+, the order of the elements is unpredictable.
+ * When the block returns zero, the order for +a+ and +b+ is indeterminate,
+ * and may be unstable:
*
- * ary = [ "d", "a", "e", "c", "b" ]
- * ary.sort! #=> ["a", "b", "c", "d", "e"]
- * ary.sort! {|a, b| b <=> a} #=> ["e", "d", "c", "b", "a"]
+ * a = 'abcde'.split('').shuffle
+ * a # => ["e", "b", "d", "a", "c"]
+ * a.sort! {|a, b| 0 }
+ * a # => ["d", "e", "c", "a", "b"]
*
- * See also Enumerable#sort_by.
*/
VALUE
@@ -2471,25 +3574,23 @@ rb_ary_sort_bang(VALUE ary)
rb_ary_modify(ary);
assert(!ARY_SHARED_P(ary));
if (RARRAY_LEN(ary) > 1) {
- VALUE tmp = ary_make_substitution(ary); /* only ary refers tmp */
- struct ary_sort_data data;
- long len = RARRAY_LEN(ary);
-
- RBASIC_CLEAR_CLASS(tmp);
- data.ary = tmp;
- data.cmp_opt.opt_methods = 0;
- data.cmp_opt.opt_inited = 0;
- RARRAY_PTR_USE(tmp, ptr, {
- ruby_qsort(ptr, len, sizeof(VALUE),
- rb_block_given_p()?sort_1:sort_2, &data);
- }); /* WB: no new reference */
- rb_ary_modify(ary);
+ VALUE tmp = ary_make_substitution(ary); /* only ary refers tmp */
+ struct ary_sort_data data;
+ long len = RARRAY_LEN(ary);
+ RBASIC_CLEAR_CLASS(tmp);
+ data.ary = tmp;
+ data.receiver = ary;
+ RARRAY_PTR_USE(tmp, ptr, {
+ ruby_qsort(ptr, len, sizeof(VALUE),
+ rb_block_given_p()?sort_1:sort_2, &data);
+ }); /* WB: no new reference */
+ rb_ary_modify(ary);
if (ARY_EMBED_P(tmp)) {
if (ARY_SHARED_P(ary)) { /* ary might be destructively operated in the given block */
rb_ary_unshare(ary);
- FL_SET_EMBED(ary);
+ FL_SET_EMBED(ary);
}
- ary_memcpy(ary, 0, ARY_EMBED_LEN(tmp), ARY_EMBED_PTR(tmp));
+ ary_memcpy(ary, 0, ARY_EMBED_LEN(tmp), ARY_EMBED_PTR(tmp));
ARY_SET_LEN(ary, ARY_EMBED_LEN(tmp));
}
else {
@@ -2507,46 +3608,65 @@ rb_ary_sort_bang(VALUE ary)
rb_ary_unshare(ary);
}
else {
- ruby_sized_xfree((void *)ARY_HEAP_PTR(ary), ARY_HEAP_SIZE(ary));
+ ary_heap_free(ary);
}
- ARY_SET_PTR(ary, RARRAY_CONST_PTR(tmp));
+ ARY_SET_PTR(ary, ARY_HEAP_PTR(tmp));
ARY_SET_HEAP_LEN(ary, len);
- ARY_SET_CAPA(ary, RARRAY_LEN(tmp));
+ ARY_SET_CAPA(ary, ARY_HEAP_LEN(tmp));
}
/* tmp was lost ownership for the ptr */
FL_UNSET(tmp, FL_FREEZE);
FL_SET_EMBED(tmp);
ARY_SET_EMBED_LEN(tmp, 0);
FL_SET(tmp, FL_FREEZE);
- }
+ }
/* tmp will be GC'ed. */
RBASIC_SET_CLASS_RAW(tmp, rb_cArray); /* rb_cArray must be marked */
}
+ ary_verify(ary);
return ary;
}
/*
* call-seq:
- * ary.sort -> new_ary
- * ary.sort {|a, b| block} -> new_ary
+ * array.sort -> new_array
+ * array.sort {|a, b| ... } -> new_array
+ *
+ * Returns a new \Array whose elements are those from +self+, sorted.
+ *
+ * With no block, compares elements using operator <tt><=></tt>
+ * (see Comparable):
+ *
+ * a = 'abcde'.split('').shuffle
+ * a # => ["e", "b", "d", "a", "c"]
+ * a1 = a.sort
+ * a1 # => ["a", "b", "c", "d", "e"]
+ *
+ * With a block, calls the block with each element pair;
+ * for each element pair +a+ and +b+, the block should return an integer:
*
- * Returns a new array created by sorting +self+.
+ * - Negative when +b+ is to follow +a+.
+ * - Zero when +a+ and +b+ are equivalent.
+ * - Positive when +a+ is to follow +b+.
*
- * Comparisons for the sort will be done using the <code><=></code> operator
- * or using an optional code block.
+ * Example:
*
- * The block must implement a comparison between +a+ and +b+ and return
- * an integer less than 0 when +b+ follows +a+, +0+ when +a+ and +b+
- * are equivalent, or an integer greater than 0 when +a+ follows +b+.
+ * a = 'abcde'.split('').shuffle
+ * a # => ["e", "b", "d", "a", "c"]
+ * a1 = a.sort {|a, b| a <=> b }
+ * a1 # => ["a", "b", "c", "d", "e"]
+ * a2 = a.sort {|a, b| b <=> a }
+ * a2 # => ["e", "d", "c", "b", "a"]
*
- * The result is not guaranteed to be stable. When the comparison of two
- * elements returns +0+, the order of the elements is unpredictable.
+ * When the block returns zero, the order for +a+ and +b+ is indeterminate,
+ * and may be unstable:
*
- * ary = [ "d", "a", "e", "c", "b" ]
- * ary.sort #=> ["a", "b", "c", "d", "e"]
- * ary.sort {|a, b| b <=> a} #=> ["e", "d", "c", "b", "a"]
+ * a = 'abcde'.split('').shuffle
+ * a # => ["e", "b", "d", "a", "c"]
+ * a1 = a.sort {|a, b| 0 }
+ * a1 # => ["c", "e", "b", "d", "a"]
*
- * See also Enumerable#sort_by.
+ * Related: Enumerable#sort_by.
*/
VALUE
@@ -2561,55 +3681,12 @@ static VALUE rb_ary_bsearch_index(VALUE ary);
/*
* call-seq:
- * ary.bsearch {|x| block } -> elem
- *
- * By using binary search, finds a value from this array which meets
- * the given condition in O(log n) where n is the size of the array.
- *
- * You can use this method in two modes: a find-minimum mode and
- * a find-any mode. In either case, the elements of the array must be
- * monotone (or sorted) with respect to the block.
- *
- * In find-minimum mode (this is a good choice for typical use cases),
- * the block must always return true or false, and there must be an index i
- * (0 <= i <= ary.size) so that:
- *
- * - the block returns false for any element whose index is less than
- * i, and
- * - the block returns true for any element whose index is greater
- * than or equal to i.
- *
- * This method returns the i-th element. If i is equal to ary.size,
- * it returns nil.
- *
- * ary = [0, 4, 7, 10, 12]
- * ary.bsearch {|x| x >= 4 } #=> 4
- * ary.bsearch {|x| x >= 6 } #=> 7
- * ary.bsearch {|x| x >= -1 } #=> 0
- * ary.bsearch {|x| x >= 100 } #=> nil
- *
- * In find-any mode (this behaves like libc's bsearch(3)), the block
- * must always return a number, and there must be two indices i and j
- * (0 <= i <= j <= ary.size) so that:
- *
- * - the block returns a positive number for ary[k] if 0 <= k < i,
- * - the block returns zero for ary[k] if i <= k < j, and
- * - the block returns a negative number for ary[k] if
- * j <= k < ary.size.
- *
- * Under this condition, this method returns any element whose index
- * is within i...j. If i is equal to j (i.e., there is no element
- * that satisfies the block), this method returns nil.
- *
- * ary = [0, 4, 7, 10, 12]
- * # try to find v such that 4 <= v < 8
- * ary.bsearch {|x| 1 - x / 4 } #=> 4 or 7
- * # try to find v such that 8 <= v < 10
- * ary.bsearch {|x| 4 - x / 2 } #=> nil
- *
- * You must not mix the two modes at a time; the block must always
- * return either true/false, or always return a number. It is
- * undefined which value is actually picked up at each iteration.
+ * array.bsearch {|element| ... } -> object
+ * array.bsearch -> new_enumerator
+ *
+ * Returns an element from +self+ selected by a binary search.
+ *
+ * See {Binary Searching}[rdoc-ref:bsearch.rdoc].
*/
static VALUE
@@ -2618,22 +3695,18 @@ rb_ary_bsearch(VALUE ary)
VALUE index_result = rb_ary_bsearch_index(ary);
if (FIXNUM_P(index_result)) {
- return rb_ary_entry(ary, FIX2LONG(index_result));
+ return rb_ary_entry(ary, FIX2LONG(index_result));
}
return index_result;
}
/*
* call-seq:
- * ary.bsearch_index {|x| block } -> int or nil
- *
- * By using binary search, finds an index of a value from this array which
- * meets the given condition in O(log n) where n is the size of the array.
+ * array.bsearch_index {|element| ... } -> integer or nil
+ * array.bsearch_index -> new_enumerator
*
- * It supports two modes, depending on the nature of the block. They are
- * exactly the same as in the case of the #bsearch method, with the only difference
- * being that this method returns the index of the element instead of the
- * element itself. For more details consult the documentation for #bsearch.
+ * Searches +self+ as described at method #bsearch,
+ * but returns the _index_ of the found element instead of the element itself.
*/
static VALUE
@@ -2645,39 +3718,39 @@ rb_ary_bsearch_index(VALUE ary)
RETURN_ENUMERATOR(ary, 0, 0);
while (low < high) {
- mid = low + ((high - low) / 2);
- val = rb_ary_entry(ary, mid);
- v = rb_yield(val);
- if (FIXNUM_P(v)) {
- if (v == INT2FIX(0)) return INT2FIX(mid);
- smaller = (SIGNED_VALUE)v < 0; /* Fixnum preserves its sign-bit */
- }
- else if (v == Qtrue) {
- satisfied = 1;
- smaller = 1;
- }
- else if (v == Qfalse || v == Qnil) {
- smaller = 0;
- }
- else if (rb_obj_is_kind_of(v, rb_cNumeric)) {
- const VALUE zero = INT2FIX(0);
- switch (rb_cmpint(rb_funcallv(v, id_cmp, 1, &zero), v, zero)) {
- case 0: return INT2FIX(mid);
- case 1: smaller = 1; break;
- case -1: smaller = 0;
- }
- }
- else {
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE
- " (must be numeric, true, false or nil)",
- rb_obj_class(v));
- }
- if (smaller) {
- high = mid;
- }
- else {
- low = mid + 1;
- }
+ mid = low + ((high - low) / 2);
+ val = rb_ary_entry(ary, mid);
+ v = rb_yield(val);
+ if (FIXNUM_P(v)) {
+ if (v == INT2FIX(0)) return INT2FIX(mid);
+ smaller = (SIGNED_VALUE)v < 0; /* Fixnum preserves its sign-bit */
+ }
+ else if (v == Qtrue) {
+ satisfied = 1;
+ smaller = 1;
+ }
+ else if (!RTEST(v)) {
+ smaller = 0;
+ }
+ else if (rb_obj_is_kind_of(v, rb_cNumeric)) {
+ const VALUE zero = INT2FIX(0);
+ switch (rb_cmpint(rb_funcallv(v, id_cmp, 1, &zero), v, zero)) {
+ case 0: return INT2FIX(mid);
+ case 1: smaller = 0; break;
+ case -1: smaller = 1;
+ }
+ }
+ else {
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE
+ " (must be numeric, true, false or nil)",
+ rb_obj_class(v));
+ }
+ if (smaller) {
+ high = mid;
+ }
+ else {
+ low = mid + 1;
+ }
}
if (!satisfied) return Qnil;
return INT2FIX(low);
@@ -2692,18 +3765,28 @@ sort_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, dummy))
/*
* call-seq:
- * ary.sort_by! {|obj| block} -> ary
- * ary.sort_by! -> Enumerator
+ * array.sort_by! {|element| ... } -> self
+ * array.sort_by! -> new_enumerator
*
- * Sorts +self+ in place using a set of keys generated by mapping the
- * values in +self+ through the given block.
+ * Sorts the elements of +self+ in place,
+ * using an ordering determined by the block; returns self.
*
- * The result is not guaranteed to be stable. When two keys are equal,
- * the order of the corresponding elements is unpredictable.
+ * Calls the block with each successive element;
+ * sorts elements based on the values returned from the block.
*
- * If no block is given, an Enumerator is returned instead.
+ * For duplicates returned by the block, the ordering is indeterminate, and may be unstable.
+ *
+ * This example sorts strings based on their sizes:
+ *
+ * a = ['aaaa', 'bbb', 'cc', 'd']
+ * a.sort_by! {|element| element.size }
+ * a # => ["d", "cc", "bbb", "aaaa"]
+ *
+ * Returns a new \Enumerator if no block given:
+ *
+ * a = ['aaaa', 'bbb', 'cc', 'd']
+ * a.sort_by! # => #<Enumerator: ["aaaa", "bbb", "cc", "d"]:sort_by!>
*
- * See also Enumerable#sort_by.
*/
static VALUE
@@ -2721,23 +3804,22 @@ rb_ary_sort_by_bang(VALUE ary)
/*
* call-seq:
- * ary.collect {|item| block} -> new_ary
- * ary.map {|item| block} -> new_ary
- * ary.collect -> Enumerator
- * ary.map -> Enumerator
- *
- * Invokes the given block once for each element of +self+.
+ * array.map {|element| ... } -> new_array
+ * array.map -> new_enumerator
*
- * Creates a new array containing the values returned by the block.
+ * Calls the block, if given, with each element of +self+;
+ * returns a new \Array whose elements are the return values from the block:
*
- * See also Enumerable#collect.
+ * a = [:foo, 'bar', 2]
+ * a1 = a.map {|element| element.class }
+ * a1 # => [Symbol, String, Integer]
*
- * If no block is given, an Enumerator is returned instead.
+ * Returns a new \Enumerator if no block given:
+ * a = [:foo, 'bar', 2]
+ * a1 = a.map
+ * a1 # => #<Enumerator: [:foo, "bar", 2]:map>
*
- * a = [ "a", "b", "c", "d" ]
- * a.collect {|x| x + "!"} #=> ["a!", "b!", "c!", "d!"]
- * a.map.with_index {|x, i| x * i} #=> ["", "b", "cc", "ddd"]
- * a #=> ["a", "b", "c", "d"]
+ * Array#collect is an alias for Array#map.
*/
static VALUE
@@ -2749,7 +3831,7 @@ rb_ary_collect(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
collect = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_ary_push(collect, rb_yield_force_blockarg(RARRAY_AREF(ary, i)));
+ rb_ary_push(collect, rb_yield(RARRAY_AREF(ary, i)));
}
return collect;
}
@@ -2757,23 +3839,22 @@ rb_ary_collect(VALUE ary)
/*
* call-seq:
- * ary.collect! {|item| block } -> ary
- * ary.map! {|item| block } -> ary
- * ary.collect! -> Enumerator
- * ary.map! -> Enumerator
+ * array.map! {|element| ... } -> self
+ * array.map! -> new_enumerator
+ *
+ * Calls the block, if given, with each element;
+ * replaces the element with the block's return value:
*
- * Invokes the given block once for each element of +self+, replacing the
- * element with the value returned by the block.
+ * a = [:foo, 'bar', 2]
+ * a.map! { |element| element.class } # => [Symbol, String, Integer]
*
- * See also Enumerable#collect.
+ * Returns a new \Enumerator if no block given:
*
- * If no block is given, an Enumerator is returned instead.
+ * a = [:foo, 'bar', 2]
+ * a1 = a.map!
+ * a1 # => #<Enumerator: [:foo, "bar", 2]:map!>
*
- * a = [ "a", "b", "c", "d" ]
- * a.map! {|x| x + "!" }
- * a #=> [ "a!", "b!", "c!", "d!" ]
- * a.collect!.with_index {|x, i| x[0...i] }
- * a #=> ["", "b", "c!", "d!"]
+ * Array#collect! is an alias for Array#map!.
*/
static VALUE
@@ -2784,7 +3865,7 @@ rb_ary_collect_bang(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
rb_ary_modify(ary);
for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_ary_store(ary, i, rb_yield(RARRAY_AREF(ary, i)));
+ rb_ary_store(ary, i, rb_yield(RARRAY_AREF(ary, i)));
}
return ary;
}
@@ -2796,21 +3877,21 @@ rb_get_values_at(VALUE obj, long olen, int argc, const VALUE *argv, VALUE (*func
long beg, len, i, j;
for (i=0; i<argc; i++) {
- if (FIXNUM_P(argv[i])) {
- rb_ary_push(result, (*func)(obj, FIX2LONG(argv[i])));
- continue;
- }
- /* check if idx is Range */
- if (rb_range_beg_len(argv[i], &beg, &len, olen, 1)) {
- long end = olen < beg+len ? olen : beg+len;
- for (j = beg; j < end; j++) {
- rb_ary_push(result, (*func)(obj, j));
- }
- if (beg + len > j)
- rb_ary_resize(result, RARRAY_LEN(result) + (beg + len) - j);
- continue;
- }
- rb_ary_push(result, (*func)(obj, NUM2LONG(argv[i])));
+ if (FIXNUM_P(argv[i])) {
+ rb_ary_push(result, (*func)(obj, FIX2LONG(argv[i])));
+ continue;
+ }
+ /* check if idx is Range */
+ if (rb_range_beg_len(argv[i], &beg, &len, olen, 1)) {
+ long end = olen < beg+len ? olen : beg+len;
+ for (j = beg; j < end; j++) {
+ rb_ary_push(result, (*func)(obj, j));
+ }
+ if (beg + len > j)
+ rb_ary_resize(result, RARRAY_LEN(result) + (beg + len) - j);
+ continue;
+ }
+ rb_ary_push(result, (*func)(obj, NUM2LONG(argv[i])));
}
return result;
}
@@ -2820,45 +3901,70 @@ append_values_at_single(VALUE result, VALUE ary, long olen, VALUE idx)
{
long beg, len;
if (FIXNUM_P(idx)) {
- beg = FIX2LONG(idx);
+ beg = FIX2LONG(idx);
}
/* check if idx is Range */
else if (rb_range_beg_len(idx, &beg, &len, olen, 1)) {
- if (len > 0) {
- const VALUE *const src = RARRAY_CONST_PTR(ary);
- const long end = beg + len;
- const long prevlen = RARRAY_LEN(result);
- if (beg < olen) {
- rb_ary_cat(result, src + beg, end > olen ? olen-beg : len);
- }
- if (end > olen) {
- rb_ary_store(result, prevlen + len - 1, Qnil);
- }
- }
- return result;
+ if (len > 0) {
+ const VALUE *const src = RARRAY_CONST_PTR_TRANSIENT(ary);
+ const long end = beg + len;
+ const long prevlen = RARRAY_LEN(result);
+ if (beg < olen) {
+ rb_ary_cat(result, src + beg, end > olen ? olen-beg : len);
+ }
+ if (end > olen) {
+ rb_ary_store(result, prevlen + len - 1, Qnil);
+ }
+ }
+ return result;
}
else {
- beg = NUM2LONG(idx);
+ beg = NUM2LONG(idx);
}
return rb_ary_push(result, rb_ary_entry(ary, beg));
}
/*
* call-seq:
- * ary.values_at(selector, ...) -> new_ary
+ * array.values_at(*indexes) -> new_array
+ *
+ * Returns a new \Array whose elements are the elements
+ * of +self+ at the given \Integer or \Range +indexes+.
+ *
+ * For each positive +index+, returns the element at offset +index+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.values_at(0, 2) # => [:foo, 2]
+ * a.values_at(0..1) # => [:foo, "bar"]
+ *
+ * The given +indexes+ may be in any order, and may repeat:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.values_at(2, 0, 1, 0, 2) # => [2, :foo, "bar", :foo, 2]
+ * a.values_at(1, 0..2) # => ["bar", :foo, "bar", 2]
*
- * Returns an array containing the elements in +self+ corresponding to the
- * given +selector+(s).
+ * Assigns +nil+ for an +index+ that is too large:
*
- * The selectors may be either integer indices or ranges.
+ * a = [:foo, 'bar', 2]
+ * a.values_at(0, 3, 1, 3) # => [:foo, nil, "bar", nil]
*
- * See also Array#select.
+ * Returns a new empty \Array if no arguments given.
+ *
+ * For each negative +index+, counts backward from the end of the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.values_at(-1, -3) # => [2, :foo]
+ *
+ * Assigns +nil+ for an +index+ that is too small:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.values_at(0, -5, 1, -6, 2) # => [:foo, nil, "bar", nil, 2]
+ *
+ * The given +indexes+ may have a mixture of signs:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.values_at(0, -2, 1, -1) # => [:foo, "bar", "bar", 2]
*
- * a = %w{ a b c d e f }
- * a.values_at(1, 3, 5) # => ["b", "d", "f"]
- * a.values_at(1, 3, 5, 7) # => ["b", "d", "f", nil]
- * a.values_at(-1, -2, -2, -7) # => ["f", "e", "e", nil]
- * a.values_at(4..6, 3...6) # => ["e", "f", nil, "d", "e", "f"]
*/
static VALUE
@@ -2867,7 +3973,7 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary)
long i, olen = RARRAY_LEN(ary);
VALUE result = rb_ary_new_capa(argc);
for (i = 0; i < argc; ++i) {
- append_values_at_single(result, ary, olen, argv[i]);
+ append_values_at_single(result, ary, olen, argv[i]);
}
RB_GC_GUARD(ary);
return result;
@@ -2876,20 +3982,23 @@ rb_ary_values_at(int argc, VALUE *argv, VALUE ary)
/*
* call-seq:
- * ary.select {|item| block} -> new_ary
- * ary.select -> Enumerator
+ * array.select {|element| ... } -> new_array
+ * array.select -> new_enumerator
*
- * Returns a new array containing all elements of +ary+
- * for which the given +block+ returns a true value.
+ * Calls the block, if given, with each element of +self+;
+ * returns a new \Array containing those elements of +self+
+ * for which the block returns a truthy value:
*
- * If no block is given, an Enumerator is returned instead.
+ * a = [:foo, 'bar', 2, :bam]
+ * a1 = a.select {|element| element.to_s.start_with?('b') }
+ * a1 # => ["bar", :bam]
*
- * [1,2,3,4,5].select {|num| num.even? } #=> [2, 4]
+ * Returns a new \Enumerator if no block given:
*
- * a = %w{ a b c d e f }
- * a.select {|v| v =~ /[aeiou]/} #=> ["a", "e"]
+ * a = [:foo, 'bar', 2, :bam]
+ * a.select # => #<Enumerator: [:foo, "bar", 2, :bam]:select>
*
- * See also Enumerable#select.
+ * Array#filter is an alias for Array#select.
*/
static VALUE
@@ -2901,9 +4010,9 @@ rb_ary_select(VALUE ary)
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
result = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
- rb_ary_push(result, rb_ary_elt(ary, i));
- }
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
+ rb_ary_push(result, rb_ary_elt(ary, i));
+ }
}
return result;
}
@@ -2921,12 +4030,12 @@ select_bang_i(VALUE a)
long i1, i2;
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); arg->len[0] = ++i1) {
- VALUE v = RARRAY_AREF(ary, i1);
- if (!RTEST(rb_yield(v))) continue;
- if (i1 != i2) {
- rb_ary_store(ary, i2, v);
- }
- arg->len[1] = ++i2;
+ VALUE v = RARRAY_AREF(ary, i1);
+ if (!RTEST(rb_yield(v))) continue;
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, v);
+ }
+ arg->len[1] = ++i2;
}
return (i1 == i2) ? Qnil : ary;
}
@@ -2940,34 +4049,40 @@ select_bang_ensure(VALUE a)
long i1 = arg->len[0], i2 = arg->len[1];
if (i2 < len && i2 < i1) {
- long tail = 0;
- if (i1 < len) {
- tail = len - i1;
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr + i2, ptr + i1, VALUE, tail);
- });
- }
- ARY_SET_LEN(ary, i2 + tail);
+ long tail = 0;
+ rb_ary_modify(ary);
+ if (i1 < len) {
+ tail = len - i1;
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMMOVE(ptr + i2, ptr + i1, VALUE, tail);
+ });
+ }
+ ARY_SET_LEN(ary, i2 + tail);
}
return ary;
}
/*
* call-seq:
- * ary.select! {|item| block } -> ary or nil
- * ary.select! -> Enumerator
+ * array.select! {|element| ... } -> self or nil
+ * array.select! -> new_enumerator
+ *
+ * Calls the block, if given with each element of +self+;
+ * removes from +self+ those elements for which the block returns +false+ or +nil+.
*
- * Invokes the given block passing in successive elements from +self+,
- * deleting elements for which the block returns a +false+ value.
+ * Returns +self+ if any elements were removed:
*
- * The array may not be changed instantly every time the block is called.
+ * a = [:foo, 'bar', 2, :bam]
+ * a.select! {|element| element.to_s.start_with?('b') } # => ["bar", :bam]
*
- * If changes were made, it will return +self+, otherwise it returns +nil+.
+ * Returns +nil+ if no elements were removed.
*
- * See also Array#keep_if
+ * Returns a new \Enumerator if no block given:
*
- * If no block is given, an Enumerator is returned instead.
+ * a = [:foo, 'bar', 2, :bam]
+ * a.select! # => #<Enumerator: [:foo, "bar", 2, :bam]:select!>
*
+ * Array#filter! is an alias for Array#select!.
*/
static VALUE
@@ -2985,18 +4100,20 @@ rb_ary_select_bang(VALUE ary)
/*
* call-seq:
- * ary.keep_if {|item| block} -> ary
- * ary.keep_if -> Enumerator
+ * array.keep_if {|element| ... } -> self
+ * array.keep_if -> new_enumeration
*
- * Deletes every element of +self+ for which the given block evaluates to
- * +false+.
+ * Retains those elements for which the block returns a truthy value;
+ * deletes all other elements; returns +self+:
*
- * See also Array#select!
+ * a = [:foo, 'bar', 2, :bam]
+ * a.keep_if {|element| element.to_s.start_with?('b') } # => ["bar", :bam]
*
- * If no block is given, an Enumerator is returned instead.
+ * Returns a new \Enumerator if no block given:
+ *
+ * a = [:foo, 'bar', 2, :bam]
+ * a.keep_if # => #<Enumerator: [:foo, "bar", 2, :bam]:keep_if>
*
- * a = %w{ a b c d e f }
- * a.keep_if {|v| v =~ /[aeiou]/} #=> ["a", "e"]
*/
static VALUE
@@ -3012,32 +4129,48 @@ ary_resize_smaller(VALUE ary, long len)
{
rb_ary_modify(ary);
if (RARRAY_LEN(ary) > len) {
- ARY_SET_LEN(ary, len);
- if (len * 2 < ARY_CAPA(ary) &&
- ARY_CAPA(ary) > ARY_DEFAULT_SIZE) {
- ary_resize_capa(ary, len * 2);
- }
+ ARY_SET_LEN(ary, len);
+ if (len * 2 < ARY_CAPA(ary) &&
+ ARY_CAPA(ary) > ARY_DEFAULT_SIZE) {
+ ary_resize_capa(ary, len * 2);
+ }
}
}
/*
* call-seq:
- * ary.delete(obj) -> item or nil
- * ary.delete(obj) {block} -> item or result of block
+ * array.delete(obj) -> deleted_object
+ * array.delete(obj) {|nosuch| ... } -> deleted_object or block_return
+ *
+ * Removes zero or more elements from +self+.
*
- * Deletes all items from +self+ that are equal to +obj+.
+ * When no block is given,
+ * removes from +self+ each element +ele+ such that <tt>ele == obj</tt>;
+ * returns the last deleted element:
*
- * Returns the last deleted item, or +nil+ if no matching item is found.
+ * s1 = 'bar'; s2 = 'bar'
+ * a = [:foo, s1, 2, s2]
+ * a.delete('bar') # => "bar"
+ * a # => [:foo, 2]
*
- * If the optional code block is given, the result of the block is returned if
- * the item is not found. (To remove +nil+ elements and get an informative
- * return value, use Array#compact!)
+ * Returns +nil+ if no elements removed.
+ *
+ * When a block is given,
+ * removes from +self+ each element +ele+ such that <tt>ele == obj</tt>.
+ *
+ * If any such elements are found, ignores the block
+ * and returns the last deleted element:
+ *
+ * s1 = 'bar'; s2 = 'bar'
+ * a = [:foo, s1, 2, s2]
+ * deleted_obj = a.delete('bar') {|obj| fail 'Cannot happen' }
+ * a # => [:foo, 2]
+ *
+ * If no such elements are found, returns the block's return value:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.delete(:nosuch) {|obj| "#{obj} not found" } # => "nosuch not found"
*
- * a = [ "a", "b", "b", "b", "c" ]
- * a.delete("b") #=> "b"
- * a #=> ["a", "c"]
- * a.delete("z") #=> nil
- * a.delete("z") {"not found"} #=> "not found"
*/
VALUE
@@ -3047,26 +4180,27 @@ rb_ary_delete(VALUE ary, VALUE item)
long i1, i2;
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); i1++) {
- VALUE e = RARRAY_AREF(ary, i1);
+ VALUE e = RARRAY_AREF(ary, i1);
- if (rb_equal(e, item)) {
- v = e;
- continue;
- }
- if (i1 != i2) {
- rb_ary_store(ary, i2, e);
- }
- i2++;
+ if (rb_equal(e, item)) {
+ v = e;
+ continue;
+ }
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, e);
+ }
+ i2++;
}
if (RARRAY_LEN(ary) == i2) {
- if (rb_block_given_p()) {
- return rb_yield(item);
- }
- return Qnil;
+ if (rb_block_given_p()) {
+ return rb_yield(item);
+ }
+ return Qnil;
}
ary_resize_smaller(ary, i2);
+ ary_verify(ary);
return v;
}
@@ -3076,18 +4210,18 @@ rb_ary_delete_same(VALUE ary, VALUE item)
long i1, i2;
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); i1++) {
- VALUE e = RARRAY_AREF(ary, i1);
+ VALUE e = RARRAY_AREF(ary, i1);
- if (e == item) {
- continue;
- }
- if (i1 != i2) {
- rb_ary_store(ary, i2, e);
- }
- i2++;
+ if (e == item) {
+ continue;
+ }
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, e);
+ }
+ i2++;
}
if (RARRAY_LEN(ary) == i2) {
- return;
+ return;
}
ary_resize_smaller(ary, i2);
@@ -3101,33 +4235,41 @@ rb_ary_delete_at(VALUE ary, long pos)
if (pos >= len) return Qnil;
if (pos < 0) {
- pos += len;
- if (pos < 0) return Qnil;
+ pos += len;
+ if (pos < 0) return Qnil;
}
rb_ary_modify(ary);
del = RARRAY_AREF(ary, pos);
- RARRAY_PTR_USE(ary, ptr, {
- MEMMOVE(ptr+pos, ptr+pos+1, VALUE, len-pos-1);
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr, {
+ MEMMOVE(ptr+pos, ptr+pos+1, VALUE, len-pos-1);
});
ARY_INCREASE_LEN(ary, -1);
-
+ ary_verify(ary);
return del;
}
/*
* call-seq:
- * ary.delete_at(index) -> obj or nil
+ * array.delete_at(index) -> deleted_object or nil
+ *
+ * Deletes an element from +self+, per the given \Integer +index+.
+ *
+ * When +index+ is non-negative, deletes the element at offset +index+:
*
- * Deletes the element at the specified +index+, returning that element, or
- * +nil+ if the +index+ is out of range.
+ * a = [:foo, 'bar', 2]
+ * a.delete_at(1) # => "bar"
+ * a # => [:foo, 2]
*
- * See also Array#slice!
+ * If index is too large, returns +nil+.
*
- * a = ["ant", "bat", "cat", "dog"]
- * a.delete_at(2) #=> "cat"
- * a #=> ["ant", "bat", "dog"]
- * a.delete_at(99) #=> nil
+ * When +index+ is negative, counts backward from the end of the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.delete_at(-2) # => "bar"
+ * a # => [:foo, 2]
+ *
+ * If +index+ is too small (far from zero), returns nil.
*/
static VALUE
@@ -3136,73 +4278,133 @@ rb_ary_delete_at_m(VALUE ary, VALUE pos)
return rb_ary_delete_at(ary, NUM2LONG(pos));
}
+static VALUE
+ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len)
+{
+ const long orig_len = RARRAY_LEN(ary);
+
+ if (len < 0) {
+ return Qnil;
+ }
+ else if (pos < -orig_len) {
+ return Qnil;
+ }
+ else if (pos < 0) {
+ pos += orig_len;
+ }
+ else if (orig_len < pos) {
+ return Qnil;
+ }
+ if (orig_len < pos + len) {
+ len = orig_len - pos;
+ }
+ if (len == 0) {
+ return rb_ary_new2(0);
+ }
+ else {
+ VALUE arg2 = rb_ary_new4(len, RARRAY_CONST_PTR_TRANSIENT(ary)+pos);
+ rb_ary_splice(ary, pos, len, 0, 0);
+ return arg2;
+ }
+}
+
/*
* call-seq:
- * ary.slice!(index) -> obj or nil
- * ary.slice!(start, length) -> new_ary or nil
- * ary.slice!(range) -> new_ary or nil
- *
- * Deletes the element(s) given by an +index+ (optionally up to +length+
- * elements) or by a +range+.
- *
- * Returns the deleted object (or objects), or +nil+ if the +index+ is out of
- * range.
- *
- * a = [ "a", "b", "c" ]
- * a.slice!(1) #=> "b"
- * a #=> ["a", "c"]
- * a.slice!(-1) #=> "c"
- * a #=> ["a"]
- * a.slice!(100) #=> nil
- * a #=> ["a"]
+ * array.slice!(n) -> object or nil
+ * array.slice!(start, length) -> new_array or nil
+ * array.slice!(range) -> new_array or nil
+ *
+ * Removes and returns elements from +self+.
+ *
+ * When the only argument is an \Integer +n+,
+ * removes and returns the _nth_ element in +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.slice!(1) # => "bar"
+ * a # => [:foo, 2]
+ *
+ * If +n+ is negative, counts backwards from the end of +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.slice!(-1) # => 2
+ * a # => [:foo, "bar"]
+ *
+ * If +n+ is out of range, returns +nil+.
+ *
+ * When the only arguments are Integers +start+ and +length+,
+ * removes +length+ elements from +self+ beginning at offset +start+;
+ * returns the deleted objects in a new \Array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.slice!(0, 2) # => [:foo, "bar"]
+ * a # => [2]
+ *
+ * If <tt>start + length</tt> exceeds the array size,
+ * removes and returns all elements from offset +start+ to the end:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.slice!(1, 50) # => ["bar", 2]
+ * a # => [:foo]
+ *
+ * If <tt>start == a.size</tt> and +length+ is non-negative,
+ * returns a new empty \Array.
+ *
+ * If +length+ is negative, returns +nil+.
+ *
+ * When the only argument is a \Range object +range+,
+ * treats <tt>range.min</tt> as +start+ above and <tt>range.size</tt> as +length+ above:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.slice!(1..2) # => ["bar", 2]
+ * a # => [:foo]
+ *
+ * If <tt>range.start == a.size</tt>, returns a new empty \Array.
+ *
+ * If <tt>range.start</tt> is larger than the array size, returns +nil+.
+ *
+ * If <tt>range.end</tt> is negative, counts backwards from the end of the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.slice!(0..-2) # => [:foo, "bar"]
+ * a # => [2]
+ *
+ * If <tt>range.start</tt> is negative,
+ * calculates the start index backwards from the end of the array:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.slice!(-2..2) # => ["bar", 2]
+ * a # => [:foo]
+ *
*/
static VALUE
rb_ary_slice_bang(int argc, VALUE *argv, VALUE ary)
{
- VALUE arg1, arg2;
- long pos, len, orig_len;
+ VALUE arg1;
+ long pos, len;
rb_ary_modify_check(ary);
+ rb_check_arity(argc, 1, 2);
+ arg1 = argv[0];
+
if (argc == 2) {
- pos = NUM2LONG(argv[0]);
- len = NUM2LONG(argv[1]);
- delete_pos_len:
- if (len < 0) return Qnil;
- orig_len = RARRAY_LEN(ary);
- if (pos < 0) {
- pos += orig_len;
- if (pos < 0) return Qnil;
- }
- else if (orig_len < pos) return Qnil;
- if (orig_len < pos + len) {
- len = orig_len - pos;
- }
- if (len == 0) return rb_ary_new2(0);
- arg2 = rb_ary_new4(len, RARRAY_CONST_PTR(ary)+pos);
- RBASIC_SET_CLASS(arg2, rb_obj_class(ary));
- rb_ary_splice(ary, pos, len, 0, 0);
- return arg2;
- }
-
- if (argc != 1) {
- /* error report */
- rb_scan_args(argc, argv, "11", NULL, NULL);
+ pos = NUM2LONG(argv[0]);
+ len = NUM2LONG(argv[1]);
+ return ary_slice_bang_by_rb_ary_splice(ary, pos, len);
}
- arg1 = argv[0];
if (!FIXNUM_P(arg1)) {
- switch (rb_range_beg_len(arg1, &pos, &len, RARRAY_LEN(ary), 0)) {
- case Qtrue:
- /* valid range */
- goto delete_pos_len;
- case Qnil:
- /* invalid range */
- return Qnil;
- default:
- /* not a range */
- break;
- }
+ switch (rb_range_beg_len(arg1, &pos, &len, RARRAY_LEN(ary), 0)) {
+ case Qtrue:
+ /* valid range */
+ return ary_slice_bang_by_rb_ary_splice(ary, pos, len);
+ case Qnil:
+ /* invalid range */
+ return Qnil;
+ default:
+ /* not a range */
+ break;
+ }
}
return rb_ary_delete_at(ary, NUM2LONG(arg1));
@@ -3214,10 +4416,11 @@ ary_reject(VALUE orig, VALUE result)
long i;
for (i = 0; i < RARRAY_LEN(orig); i++) {
- VALUE v = RARRAY_AREF(orig, i);
- if (!RTEST(rb_yield(v))) {
- rb_ary_push(result, v);
- }
+ VALUE v = RARRAY_AREF(orig, i);
+
+ if (!RTEST(rb_yield(v))) {
+ rb_ary_push(result, v);
+ }
}
return result;
}
@@ -3230,12 +4433,12 @@ reject_bang_i(VALUE a)
long i1, i2;
for (i1 = i2 = 0; i1 < RARRAY_LEN(ary); arg->len[0] = ++i1) {
- VALUE v = RARRAY_AREF(ary, i1);
- if (RTEST(rb_yield(v))) continue;
- if (i1 != i2) {
- rb_ary_store(ary, i2, v);
- }
- arg->len[1] = ++i2;
+ VALUE v = RARRAY_AREF(ary, i1);
+ if (RTEST(rb_yield(v))) continue;
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, v);
+ }
+ arg->len[1] = ++i2;
}
return (i1 == i2) ? Qnil : ary;
}
@@ -3244,7 +4447,6 @@ static VALUE
ary_reject_bang(VALUE ary)
{
struct select_bang_arg args;
-
rb_ary_modify_check(ary);
args.ary = ary;
args.len[0] = args.len[1] = 0;
@@ -3253,37 +4455,50 @@ ary_reject_bang(VALUE ary)
/*
* call-seq:
- * ary.reject! {|item| block} -> ary or nil
- * ary.reject! -> Enumerator
+ * array.reject! {|element| ... } -> self or nil
+ * array.reject! -> new_enumerator
+ *
+ * Removes each element for which the block returns a truthy value.
+ *
+ * Returns +self+ if any elements removed:
+ *
+ * a = [:foo, 'bar', 2, 'bat']
+ * a.reject! {|element| element.to_s.start_with?('b') } # => [:foo, 2]
*
- * Deletes every element of +self+ for which the block evaluates to +true+,
- * if no changes were made returns +nil+.
+ * Returns +nil+ if no elements removed.
*
- * The array may not be changed instantly every time the block is called.
+ * Returns a new \Enumerator if no block given:
*
- * See also Enumerable#reject and Array#delete_if.
+ * a = [:foo, 'bar', 2]
+ * a.reject! # => #<Enumerator: [:foo, "bar", 2]:reject!>
*
- * If no block is given, an Enumerator is returned instead.
*/
static VALUE
rb_ary_reject_bang(VALUE ary)
{
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
+ rb_ary_modify(ary);
return ary_reject_bang(ary);
}
/*
* call-seq:
- * ary.reject {|item| block } -> new_ary
- * ary.reject -> Enumerator
+ * array.reject {|element| ... } -> new_array
+ * array.reject -> new_enumerator
+ *
+ * Returns a new \Array whose elements are all those from +self+
+ * for which the block returns +false+ or +nil+:
+ *
+ * a = [:foo, 'bar', 2, 'bat']
+ * a1 = a.reject {|element| element.to_s.start_with?('b') }
+ * a1 # => [:foo, 2]
*
- * Returns a new array containing the items in +self+ for which the given
- * block is not +true+. The ordering of non-rejected elements is maintained.
+ * Returns a new \Enumerator if no block given:
*
- * See also Array#delete_if
+ * a = [:foo, 'bar', 2]
+ * a.reject # => #<Enumerator: [:foo, "bar", 2]:reject>
*
- * If no block is given, an Enumerator is returned instead.
*/
static VALUE
@@ -3299,25 +4514,26 @@ rb_ary_reject(VALUE ary)
/*
* call-seq:
- * ary.delete_if {|item| block} -> ary
- * ary.delete_if -> Enumerator
+ * array.delete_if {|element| ... } -> self
+ * array.delete_if -> Enumerator
*
- * Deletes every element of +self+ for which block evaluates to +true+.
+ * Removes each element in +self+ for which the block returns a truthy value;
+ * returns +self+:
*
- * The array is changed instantly every time the block is called, not after
- * the iteration is over.
+ * a = [:foo, 'bar', 2, 'bat']
+ * a.delete_if {|element| element.to_s.start_with?('b') } # => [:foo, 2]
*
- * See also Array#reject!
+ * Returns a new \Enumerator if no block given:
*
- * If no block is given, an Enumerator is returned instead.
+ * a = [:foo, 'bar', 2]
+ * a.delete_if # => #<Enumerator: [:foo, "bar", 2]:delete_if>
*
- * scores = [ 97, 42, 75 ]
- * scores.delete_if {|score| score < 80 } #=> [97]
- */
+3 */
static VALUE
rb_ary_delete_if(VALUE ary)
{
+ ary_verify(ary);
RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
ary_reject_bang(ary);
return ary;
@@ -3327,9 +4543,9 @@ static VALUE
take_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, cbarg))
{
VALUE *args = (VALUE *)cbarg;
- if (args[1]-- == 0) rb_iter_break();
if (argc > 1) val = rb_ary_new4(argc, argv);
rb_ary_push(args[0], val);
+ if (--args[1] == 0) rb_iter_break();
return Qnil;
}
@@ -3339,38 +4555,71 @@ take_items(VALUE obj, long n)
VALUE result = rb_check_array_type(obj);
VALUE args[2];
+ if (n == 0) return result;
if (!NIL_P(result)) return rb_ary_subseq(result, 0, n);
result = rb_ary_new2(n);
args[0] = result; args[1] = (VALUE)n;
- if (rb_check_block_call(obj, idEach, 0, 0, take_i, (VALUE)args) == Qundef)
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
- rb_obj_class(obj));
+ if (UNDEF_P(rb_check_block_call(obj, idEach, 0, 0, take_i, (VALUE)args)))
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",
+ rb_obj_class(obj));
return result;
}
/*
* call-seq:
- * ary.zip(arg, ...) -> new_ary
- * ary.zip(arg, ...) {|arr| block} -> nil
+ * array.zip(*other_arrays) -> new_array
+ * array.zip(*other_arrays) {|other_array| ... } -> nil
+ *
+ * When no block given, returns a new \Array +new_array+ of size <tt>self.size</tt>
+ * whose elements are Arrays.
+ *
+ * Each nested array <tt>new_array[n]</tt> is of size <tt>other_arrays.size+1</tt>,
+ * and contains:
*
- * Converts any arguments to arrays, then merges elements of +self+ with
- * corresponding elements from each argument.
+ * - The _nth_ element of +self+.
+ * - The _nth_ element of each of the +other_arrays+.
*
- * This generates a sequence of <code>ary.size</code> _n_-element arrays,
- * where _n_ is one more than the count of arguments.
+ * If all +other_arrays+ and +self+ are the same size:
*
- * If the size of any argument is less than the size of the initial array,
- * +nil+ values are supplied.
+ * a = [:a0, :a1, :a2, :a3]
+ * b = [:b0, :b1, :b2, :b3]
+ * c = [:c0, :c1, :c2, :c3]
+ * d = a.zip(b, c)
+ * d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]]
*
- * If a block is given, it is invoked for each output +array+, otherwise an
- * array of arrays is returned.
+ * If any array in +other_arrays+ is smaller than +self+,
+ * fills to <tt>self.size</tt> with +nil+:
+ *
+ * a = [:a0, :a1, :a2, :a3]
+ * b = [:b0, :b1, :b2]
+ * c = [:c0, :c1]
+ * d = a.zip(b, c)
+ * d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, nil], [:a3, nil, nil]]
+ *
+ * If any array in +other_arrays+ is larger than +self+,
+ * its trailing elements are ignored:
+ *
+ * a = [:a0, :a1, :a2, :a3]
+ * b = [:b0, :b1, :b2, :b3, :b4]
+ * c = [:c0, :c1, :c2, :c3, :c4, :c5]
+ * d = a.zip(b, c)
+ * d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]]
+ *
+ * When a block is given, calls the block with each of the sub-arrays (formed as above); returns +nil+:
+ *
+ * a = [:a0, :a1, :a2, :a3]
+ * b = [:b0, :b1, :b2, :b3]
+ * c = [:c0, :c1, :c2, :c3]
+ * a.zip(b, c) {|sub_array| p sub_array} # => nil
+ *
+ * Output:
+ *
+ * [:a0, :b0, :c0]
+ * [:a1, :b1, :c1]
+ * [:a2, :b2, :c2]
+ * [:a3, :b3, :c3]
*
- * a = [ 4, 5, 6 ]
- * b = [ 7, 8, 9 ]
- * [1, 2, 3].zip(a, b) #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
- * [1, 2].zip(a, b) #=> [[1, 4, 7], [2, 5, 8]]
- * a.zip([1, 2], [8]) #=> [[4, 1, 8], [5, 2, nil], [6, nil, nil]]
*/
static VALUE
@@ -3381,51 +4630,51 @@ rb_ary_zip(int argc, VALUE *argv, VALUE ary)
VALUE result = Qnil;
for (i=0; i<argc; i++) {
- argv[i] = take_items(argv[i], len);
+ argv[i] = take_items(argv[i], len);
}
if (rb_block_given_p()) {
- int arity = rb_block_arity();
-
- if (arity > 1) {
- VALUE work, *tmp;
-
- tmp = ALLOCV_N(VALUE, work, argc+1);
-
- for (i=0; i<RARRAY_LEN(ary); i++) {
- tmp[0] = RARRAY_AREF(ary, i);
- for (j=0; j<argc; j++) {
- tmp[j+1] = rb_ary_elt(argv[j], i);
- }
- rb_yield_values2(argc+1, tmp);
- }
-
- if (work) ALLOCV_END(work);
- }
- else {
- for (i=0; i<RARRAY_LEN(ary); i++) {
- VALUE tmp = rb_ary_new2(argc+1);
-
- rb_ary_push(tmp, RARRAY_AREF(ary, i));
- for (j=0; j<argc; j++) {
- rb_ary_push(tmp, rb_ary_elt(argv[j], i));
- }
- rb_yield(tmp);
- }
- }
+ int arity = rb_block_arity();
+
+ if (arity > 1) {
+ VALUE work, *tmp;
+
+ tmp = ALLOCV_N(VALUE, work, argc+1);
+
+ for (i=0; i<RARRAY_LEN(ary); i++) {
+ tmp[0] = RARRAY_AREF(ary, i);
+ for (j=0; j<argc; j++) {
+ tmp[j+1] = rb_ary_elt(argv[j], i);
+ }
+ rb_yield_values2(argc+1, tmp);
+ }
+
+ if (work) ALLOCV_END(work);
+ }
+ else {
+ for (i=0; i<RARRAY_LEN(ary); i++) {
+ VALUE tmp = rb_ary_new2(argc+1);
+
+ rb_ary_push(tmp, RARRAY_AREF(ary, i));
+ for (j=0; j<argc; j++) {
+ rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+ }
+ rb_yield(tmp);
+ }
+ }
}
else {
- result = rb_ary_new_capa(len);
+ result = rb_ary_new_capa(len);
- for (i=0; i<len; i++) {
- VALUE tmp = rb_ary_new_capa(argc+1);
+ for (i=0; i<len; i++) {
+ VALUE tmp = rb_ary_new_capa(argc+1);
- rb_ary_push(tmp, RARRAY_AREF(ary, i));
- for (j=0; j<argc; j++) {
- rb_ary_push(tmp, rb_ary_elt(argv[j], i));
- }
- rb_ary_push(result, tmp);
- }
+ rb_ary_push(tmp, RARRAY_AREF(ary, i));
+ for (j=0; j<argc; j++) {
+ rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+ }
+ rb_ary_push(result, tmp);
+ }
}
return result;
@@ -3433,15 +4682,14 @@ rb_ary_zip(int argc, VALUE *argv, VALUE ary)
/*
* call-seq:
- * ary.transpose -> new_ary
+ * array.transpose -> new_array
*
- * Assumes that +self+ is an array of arrays and transposes the rows and
- * columns.
+ * Transposes the rows and columns in an \Array of Arrays;
+ * the nested Arrays must all be the same size:
*
- * a = [[1,2], [3,4], [5,6]]
- * a.transpose #=> [[1, 3, 5], [2, 4, 6]]
+ * a = [[:a0, :a1], [:b0, :b1], [:c0, :c1]]
+ * a.transpose # => [[:a0, :b0, :c0], [:a1, :b1, :c1]]
*
- * If the length of the subarrays don't match, an IndexError is raised.
*/
static VALUE
@@ -3453,36 +4701,34 @@ rb_ary_transpose(VALUE ary)
alen = RARRAY_LEN(ary);
if (alen == 0) return rb_ary_dup(ary);
for (i=0; i<alen; i++) {
- tmp = to_ary(rb_ary_elt(ary, i));
- if (elen < 0) { /* first element */
- elen = RARRAY_LEN(tmp);
- result = rb_ary_new2(elen);
- for (j=0; j<elen; j++) {
- rb_ary_store(result, j, rb_ary_new2(alen));
- }
- }
- else if (elen != RARRAY_LEN(tmp)) {
- rb_raise(rb_eIndexError, "element size differs (%ld should be %ld)",
- RARRAY_LEN(tmp), elen);
- }
- for (j=0; j<elen; j++) {
- rb_ary_store(rb_ary_elt(result, j), i, rb_ary_elt(tmp, j));
- }
+ tmp = to_ary(rb_ary_elt(ary, i));
+ if (elen < 0) { /* first element */
+ elen = RARRAY_LEN(tmp);
+ result = rb_ary_new2(elen);
+ for (j=0; j<elen; j++) {
+ rb_ary_store(result, j, rb_ary_new2(alen));
+ }
+ }
+ else if (elen != RARRAY_LEN(tmp)) {
+ rb_raise(rb_eIndexError, "element size differs (%ld should be %ld)",
+ RARRAY_LEN(tmp), elen);
+ }
+ for (j=0; j<elen; j++) {
+ rb_ary_store(rb_ary_elt(result, j), i, rb_ary_elt(tmp, j));
+ }
}
return result;
}
/*
* call-seq:
- * ary.replace(other_ary) -> ary
- * ary.initialize_copy(other_ary) -> ary
+ * array.replace(other_array) -> self
*
- * Replaces the contents of +self+ with the contents of +other_ary+,
- * truncating or expanding if necessary.
+ * Replaces the content of +self+ with the content of +other_array+; returns +self+:
+ *
+ * a = [:foo, 'bar', 2]
+ * a.replace(['foo', :bar, 3]) # => ["foo", :bar, 3]
*
- * a = [ "a", "b", "c", "d", "e" ]
- * a.replace([ "x", "y", "z" ]) #=> ["x", "y", "z"]
- * a #=> ["x", "y", "z"]
*/
VALUE
@@ -3492,94 +4738,270 @@ rb_ary_replace(VALUE copy, VALUE orig)
orig = to_ary(orig);
if (copy == orig) return copy;
- if (RARRAY_LEN(orig) <= RARRAY_EMBED_LEN_MAX) {
- VALUE shared = 0;
+ rb_ary_reset(copy);
- if (ARY_OWNS_HEAP_P(copy)) {
- RARRAY_PTR_USE(copy, ptr, ruby_sized_xfree(ptr, ARY_HEAP_SIZE(copy)));
- }
- else if (ARY_SHARED_P(copy)) {
- shared = ARY_SHARED(copy);
- FL_UNSET_SHARED(copy);
- }
- FL_SET_EMBED(copy);
- ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR(orig));
- if (shared) {
- rb_ary_decrement_share(shared);
- }
- ARY_SET_LEN(copy, RARRAY_LEN(orig));
+ /* orig has enough space to embed the contents of orig. */
+ if (RARRAY_LEN(orig) <= ary_embed_capa(copy)) {
+ assert(ARY_EMBED_P(copy));
+ ary_memcpy(copy, 0, RARRAY_LEN(orig), RARRAY_CONST_PTR_TRANSIENT(orig));
+ ARY_SET_EMBED_LEN(copy, RARRAY_LEN(orig));
+ }
+#if USE_RVARGC
+ /* orig is embedded but copy does not have enough space to embed the
+ * contents of orig. */
+ else if (ARY_EMBED_P(orig)) {
+ long len = ARY_EMBED_LEN(orig);
+ VALUE *ptr = ary_heap_alloc(copy, len);
+
+ FL_UNSET_EMBED(copy);
+ ARY_SET_PTR(copy, ptr);
+ ARY_SET_LEN(copy, len);
+ ARY_SET_CAPA(copy, len);
+
+ // No allocation and exception expected that could leave `copy` in a
+ // bad state from the edits above.
+ ary_memcpy(copy, 0, len, RARRAY_CONST_PTR_TRANSIENT(orig));
}
+#endif
+ /* Otherwise, orig is on heap and copy does not have enough space to embed
+ * the contents of orig. */
else {
- VALUE shared = ary_make_shared(orig);
- if (ARY_OWNS_HEAP_P(copy)) {
- RARRAY_PTR_USE(copy, ptr, ruby_sized_xfree(ptr, ARY_HEAP_SIZE(copy)));
- }
- else {
- rb_ary_unshare_safe(copy);
- }
+ VALUE shared_root = ary_make_shared(orig);
FL_UNSET_EMBED(copy);
- ARY_SET_PTR(copy, RARRAY_CONST_PTR(orig));
- ARY_SET_LEN(copy, RARRAY_LEN(orig));
- rb_ary_set_shared(copy, shared);
+ ARY_SET_PTR(copy, ARY_HEAP_PTR(orig));
+ ARY_SET_LEN(copy, ARY_HEAP_LEN(orig));
+ rb_ary_set_shared(copy, shared_root);
}
+ ary_verify(copy);
return copy;
}
/*
* call-seq:
- * ary.clear -> ary
+ * array.clear -> self
+ *
+ * Removes all elements from +self+:
*
- * Removes all elements from +self+.
+ * a = [:foo, 'bar', 2]
+ * a.clear # => []
*
- * a = [ "a", "b", "c", "d", "e" ]
- * a.clear #=> [ ]
*/
VALUE
rb_ary_clear(VALUE ary)
{
rb_ary_modify_check(ary);
- ARY_SET_LEN(ary, 0);
if (ARY_SHARED_P(ary)) {
- if (!ARY_EMBED_P(ary)) {
- rb_ary_unshare(ary);
- FL_SET_EMBED(ary);
- }
+ if (!ARY_EMBED_P(ary)) {
+ rb_ary_unshare(ary);
+ FL_SET_EMBED(ary);
+ ARY_SET_EMBED_LEN(ary, 0);
+ }
}
- else if (ARY_DEFAULT_SIZE * 2 < ARY_CAPA(ary)) {
- ary_resize_capa(ary, ARY_DEFAULT_SIZE * 2);
+ else {
+ ARY_SET_LEN(ary, 0);
+ if (ARY_DEFAULT_SIZE * 2 < ARY_CAPA(ary)) {
+ ary_resize_capa(ary, ARY_DEFAULT_SIZE * 2);
+ }
}
+ ary_verify(ary);
return ary;
}
/*
* call-seq:
- * ary.fill(obj) -> ary
- * ary.fill(obj, start [, length]) -> ary
- * ary.fill(obj, range) -> ary
- * ary.fill {|index| block} -> ary
- * ary.fill(start [, length]) {|index| block} -> ary
- * ary.fill(range) {|index| block} -> ary
+ * array.fill(obj) -> self
+ * array.fill(obj, start) -> self
+ * array.fill(obj, start, length) -> self
+ * array.fill(obj, range) -> self
+ * array.fill {|index| ... } -> self
+ * array.fill(start) {|index| ... } -> self
+ * array.fill(start, length) {|index| ... } -> self
+ * array.fill(range) {|index| ... } -> self
+ *
+ * Replaces specified elements in +self+ with specified objects; returns +self+.
+ *
+ * With argument +obj+ and no block given, replaces all elements with that one object:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a # => ["a", "b", "c", "d"]
+ * a.fill(:X) # => [:X, :X, :X, :X]
+ *
+ * With arguments +obj+ and \Integer +start+, and no block given,
+ * replaces elements based on the given start.
+ *
+ * If +start+ is in range (<tt>0 <= start < array.size</tt>),
+ * replaces all elements from offset +start+ through the end:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, 2) # => ["a", "b", :X, :X]
+ *
+ * If +start+ is too large (<tt>start >= array.size</tt>), does nothing:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, 4) # => ["a", "b", "c", "d"]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, 5) # => ["a", "b", "c", "d"]
+ *
+ * If +start+ is negative, counts from the end (starting index is <tt>start + array.size</tt>):
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, -2) # => ["a", "b", :X, :X]
+ *
+ * If +start+ is too small (less than and far from zero), replaces all elements:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, -6) # => [:X, :X, :X, :X]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, -50) # => [:X, :X, :X, :X]
+ *
+ * With arguments +obj+, \Integer +start+, and \Integer +length+, and no block given,
+ * replaces elements based on the given +start+ and +length+.
+ *
+ * If +start+ is in range, replaces +length+ elements beginning at offset +start+:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, 1, 1) # => ["a", :X, "c", "d"]
+ *
+ * If +start+ is negative, counts from the end:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, -2, 1) # => ["a", "b", :X, "d"]
+ *
+ * If +start+ is large (<tt>start >= array.size</tt>), extends +self+ with +nil+:
*
- * The first three forms set the selected elements of +self+ (which
- * may be the entire array) to +obj+.
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, 5, 0) # => ["a", "b", "c", "d", nil]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, 5, 2) # => ["a", "b", "c", "d", nil, :X, :X]
*
- * A +start+ of +nil+ is equivalent to zero.
+ * If +length+ is zero or negative, replaces no elements:
*
- * A +length+ of +nil+ is equivalent to the length of the array.
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, 1, 0) # => ["a", "b", "c", "d"]
+ * a.fill(:X, 1, -1) # => ["a", "b", "c", "d"]
*
- * The last three forms fill the array with the value of the given block,
- * which is passed the absolute index of each element to be filled.
+ * With arguments +obj+ and \Range +range+, and no block given,
+ * replaces elements based on the given range.
*
- * Negative values of +start+ count from the end of the array, where +-1+ is
- * the last element.
+ * If the range is positive and ascending (<tt>0 < range.begin <= range.end</tt>),
+ * replaces elements from <tt>range.begin</tt> to <tt>range.end</tt>:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, (1..1)) # => ["a", :X, "c", "d"]
+ *
+ * If <tt>range.first</tt> is negative, replaces no elements:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, (-1..1)) # => ["a", "b", "c", "d"]
+ *
+ * If <tt>range.last</tt> is negative, counts from the end:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, (0..-2)) # => [:X, :X, :X, "d"]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, (1..-2)) # => ["a", :X, :X, "d"]
+ *
+ * If <tt>range.last</tt> and <tt>range.last</tt> are both negative,
+ * both count from the end of the array:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, (-1..-1)) # => ["a", "b", "c", :X]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(:X, (-2..-2)) # => ["a", "b", :X, "d"]
+ *
+ * With no arguments and a block given, calls the block with each index;
+ * replaces the corresponding element with the block's return value:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "new_3"]
+ *
+ * With argument +start+ and a block given, calls the block with each index
+ * from offset +start+ to the end; replaces the corresponding element
+ * with the block's return value.
+ *
+ * If start is in range (<tt>0 <= start < array.size</tt>),
+ * replaces from offset +start+ to the end:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(1) { |index| "new_#{index}" } # => ["a", "new_1", "new_2", "new_3"]
+ *
+ * If +start+ is too large(<tt>start >= array.size</tt>), does nothing:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(4) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(4) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"]
+ *
+ * If +start+ is negative, counts from the end:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(-2) { |index| "new_#{index}" } # => ["a", "b", "new_2", "new_3"]
+ *
+ * If start is too small (<tt>start <= -array.size</tt>, replaces all elements:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(-6) { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "new_3"]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(-50) { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "new_3"]
+ *
+ * With arguments +start+ and +length+, and a block given,
+ * calls the block for each index specified by start length;
+ * replaces the corresponding element with the block's return value.
+ *
+ * If +start+ is in range, replaces +length+ elements beginning at offset +start+:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(1, 1) { |index| "new_#{index}" } # => ["a", "new_1", "c", "d"]
+ *
+ * If start is negative, counts from the end:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(-2, 1) { |index| "new_#{index}" } # => ["a", "b", "new_2", "d"]
+ *
+ * If +start+ is large (<tt>start >= array.size</tt>), extends +self+ with +nil+:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(5, 0) { |index| "new_#{index}" } # => ["a", "b", "c", "d", nil]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(5, 2) { |index| "new_#{index}" } # => ["a", "b", "c", "d", nil, "new_5", "new_6"]
+ *
+ * If +length+ is zero or less, replaces no elements:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(1, 0) { |index| "new_#{index}" } # => ["a", "b", "c", "d"]
+ * a.fill(1, -1) { |index| "new_#{index}" } # => ["a", "b", "c", "d"]
+ *
+ * With arguments +obj+ and +range+, and a block given,
+ * calls the block with each index in the given range;
+ * replaces the corresponding element with the block's return value.
+ *
+ * If the range is positive and ascending (<tt>range 0 < range.begin <= range.end</tt>,
+ * replaces elements from <tt>range.begin</tt> to <tt>range.end</tt>:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(1..1) { |index| "new_#{index}" } # => ["a", "new_1", "c", "d"]
+ *
+ * If +range.first+ is negative, does nothing:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(-1..1) { |index| fail 'Cannot happen' } # => ["a", "b", "c", "d"]
+ *
+ * If <tt>range.last</tt> is negative, counts from the end:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(0..-2) { |index| "new_#{index}" } # => ["new_0", "new_1", "new_2", "d"]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(1..-2) { |index| "new_#{index}" } # => ["a", "new_1", "new_2", "d"]
+ *
+ * If <tt>range.first</tt> and <tt>range.last</tt> are both negative,
+ * both count from the end:
+ *
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(-1..-1) { |index| "new_#{index}" } # => ["a", "b", "c", "new_3"]
+ * a = ['a', 'b', 'c', 'd']
+ * a.fill(-2..-2) { |index| "new_#{index}" } # => ["a", "b", "new_2", "d"]
*
- * a = [ "a", "b", "c", "d" ]
- * a.fill("x") #=> ["x", "x", "x", "x"]
- * a.fill("z", 2, 2) #=> ["x", "x", "z", "z"]
- * a.fill("y", 0..1) #=> ["y", "y", "z", "z"]
- * a.fill {|i| i*i} #=> [0, 1, 4, 9]
- * a.fill(-2) {|i| i*i*i} #=> [0, 1, 8, 27]
*/
static VALUE
@@ -3589,84 +5011,74 @@ rb_ary_fill(int argc, VALUE *argv, VALUE ary)
long beg = 0, end = 0, len = 0;
if (rb_block_given_p()) {
- rb_scan_args(argc, argv, "02", &arg1, &arg2);
- argc += 1; /* hackish */
+ rb_scan_args(argc, argv, "02", &arg1, &arg2);
+ argc += 1; /* hackish */
}
else {
- rb_scan_args(argc, argv, "12", &item, &arg1, &arg2);
+ rb_scan_args(argc, argv, "12", &item, &arg1, &arg2);
}
switch (argc) {
case 1:
- beg = 0;
- len = RARRAY_LEN(ary);
- break;
+ beg = 0;
+ len = RARRAY_LEN(ary);
+ break;
case 2:
- if (rb_range_beg_len(arg1, &beg, &len, RARRAY_LEN(ary), 1)) {
- break;
- }
- /* fall through */
+ if (rb_range_beg_len(arg1, &beg, &len, RARRAY_LEN(ary), 1)) {
+ break;
+ }
+ /* fall through */
case 3:
- beg = NIL_P(arg1) ? 0 : NUM2LONG(arg1);
- if (beg < 0) {
- beg = RARRAY_LEN(ary) + beg;
- if (beg < 0) beg = 0;
- }
- len = NIL_P(arg2) ? RARRAY_LEN(ary) - beg : NUM2LONG(arg2);
- break;
+ beg = NIL_P(arg1) ? 0 : NUM2LONG(arg1);
+ if (beg < 0) {
+ beg = RARRAY_LEN(ary) + beg;
+ if (beg < 0) beg = 0;
+ }
+ len = NIL_P(arg2) ? RARRAY_LEN(ary) - beg : NUM2LONG(arg2);
+ break;
}
rb_ary_modify(ary);
if (len < 0) {
return ary;
}
if (beg >= ARY_MAX_SIZE || len > ARY_MAX_SIZE - beg) {
- rb_raise(rb_eArgError, "argument too big");
+ rb_raise(rb_eArgError, "argument too big");
}
end = beg + len;
if (RARRAY_LEN(ary) < end) {
- if (end >= ARY_CAPA(ary)) {
- ary_resize_capa(ary, end);
- }
- ary_mem_clear(ary, RARRAY_LEN(ary), end - RARRAY_LEN(ary));
- ARY_SET_LEN(ary, end);
+ if (end >= ARY_CAPA(ary)) {
+ ary_resize_capa(ary, end);
+ }
+ ary_mem_clear(ary, RARRAY_LEN(ary), end - RARRAY_LEN(ary));
+ ARY_SET_LEN(ary, end);
}
- if (item == Qundef) {
- VALUE v;
- long i;
+ if (UNDEF_P(item)) {
+ VALUE v;
+ long i;
- for (i=beg; i<end; i++) {
- v = rb_yield(LONG2NUM(i));
- if (i>=RARRAY_LEN(ary)) break;
- ARY_SET(ary, i, v);
- }
+ for (i=beg; i<end; i++) {
+ v = rb_yield(LONG2NUM(i));
+ if (i>=RARRAY_LEN(ary)) break;
+ ARY_SET(ary, i, v);
+ }
}
else {
- ary_memfill(ary, beg, len, item);
+ ary_memfill(ary, beg, len, item);
}
return ary;
}
/*
* call-seq:
- * ary + other_ary -> new_ary
- *
- * Concatenation --- Returns a new array built by concatenating the
- * two arrays together to produce a third array.
+ * array + other_array -> new_array
*
- * [ 1, 2, 3 ] + [ 4, 5 ] #=> [ 1, 2, 3, 4, 5 ]
- * a = [ "a", "b", "c" ]
- * c = a + [ "d", "e", "f" ]
- * c #=> [ "a", "b", "c", "d", "e", "f" ]
- * a #=> [ "a", "b", "c" ]
+ * Returns a new \Array containing all elements of +array+
+ * followed by all elements of +other_array+:
*
- * Note that
- * x += y
- * is the same as
- * x = x + y
- * This means that it produces a new array. As a consequence,
- * repeated use of <code>+=</code> on arrays can be quite inefficient.
+ * a = [0, 1] + [2, 3]
+ * a # => [0, 1, 2, 3]
*
- * See also Array#concat.
+ * Related: #concat.
*/
VALUE
@@ -3681,8 +5093,8 @@ rb_ary_plus(VALUE x, VALUE y)
len = xlen + ylen;
z = rb_ary_new2(len);
- ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR(x));
- ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR(y));
+ ary_memcpy(z, 0, xlen, RARRAY_CONST_PTR_TRANSIENT(x));
+ ary_memcpy(z, xlen, ylen, RARRAY_CONST_PTR_TRANSIENT(y));
ARY_SET_LEN(z, len);
return z;
}
@@ -3692,29 +5104,20 @@ ary_append(VALUE x, VALUE y)
{
long n = RARRAY_LEN(y);
if (n > 0) {
- rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR(y), n);
+ rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR_TRANSIENT(y), n);
}
+ RB_GC_GUARD(y);
return x;
}
/*
* call-seq:
- * ary.concat(other_ary1, other_ary2,...) -> ary
- *
- * Appends the elements of +other_ary+s to +self+.
- *
- * [ "a", "b" ].concat( ["c", "d"]) #=> [ "a", "b", "c", "d" ]
- * [ "a" ].concat( ["b"], ["c", "d"]) #=> [ "a", "b", "c", "d" ]
- * [ "a" ].concat #=> [ "a" ]
- *
- * a = [ 1, 2, 3 ]
- * a.concat( [ 4, 5 ])
- * a #=> [ 1, 2, 3, 4, 5 ]
+ * array.concat(*other_arrays) -> self
*
- * a = [ 1, 2 ]
- * a.concat(a, a) #=> [1, 2, 1, 2, 1, 2]
+ * Adds to +array+ all elements from each \Array in +other_arrays+; returns +self+:
*
- * See also Array#+.
+ * a = [0, 1]
+ * a.concat([2, 3], [4, 5]) # => [0, 1, 2, 3, 4, 5]
*/
static VALUE
@@ -3723,17 +5126,18 @@ rb_ary_concat_multi(int argc, VALUE *argv, VALUE ary)
rb_ary_modify_check(ary);
if (argc == 1) {
- rb_ary_concat(ary, argv[0]);
+ rb_ary_concat(ary, argv[0]);
}
else if (argc > 1) {
- int i;
- VALUE args = rb_ary_tmp_new(argc);
- for (i = 0; i < argc; i++) {
- rb_ary_concat(args, argv[i]);
- }
- ary_append(ary, args);
+ int i;
+ VALUE args = rb_ary_hidden_new(argc);
+ for (i = 0; i < argc; i++) {
+ rb_ary_concat(args, argv[i]);
+ }
+ ary_append(ary, args);
}
+ ary_verify(ary);
return ary;
}
@@ -3745,18 +5149,19 @@ rb_ary_concat(VALUE x, VALUE y)
/*
* call-seq:
- * ary * int -> new_ary
- * ary * str -> new_string
+ * array * n -> new_array
+ * array * string_separator -> new_string
*
- * Repetition --- With a String argument, equivalent to
- * <code>ary.join(str)</code>.
+ * When non-negative argument \Integer +n+ is given,
+ * returns a new \Array built by concatenating the +n+ copies of +self+:
*
- * Otherwise, returns a new array built by concatenating the +int+ copies of
- * +self+.
+ * a = ['x', 'y']
+ * a * 3 # => ["x", "y", "x", "y", "x", "y"]
*
+ * When \String argument +string_separator+ is given,
+ * equivalent to <tt>array.join(string_separator)</tt>:
*
- * [ 1, 2, 3 ] * 3 #=> [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ]
- * [ 1, 2, 3 ] * "," #=> "1,2,3"
+ * [0, [0, 1], {foo: 0}] * ', ' # => "0, 0, 1, {:foo=>0}"
*
*/
@@ -3769,61 +5174,54 @@ rb_ary_times(VALUE ary, VALUE times)
tmp = rb_check_string_type(times);
if (!NIL_P(tmp)) {
- return rb_ary_join(ary, tmp);
+ return rb_ary_join(ary, tmp);
}
len = NUM2LONG(times);
if (len == 0) {
- ary2 = ary_new(rb_obj_class(ary), 0);
- goto out;
+ ary2 = ary_new(rb_cArray, 0);
+ goto out;
}
if (len < 0) {
- rb_raise(rb_eArgError, "negative argument");
+ rb_raise(rb_eArgError, "negative argument");
}
if (ARY_MAX_SIZE/len < RARRAY_LEN(ary)) {
- rb_raise(rb_eArgError, "argument too big");
+ rb_raise(rb_eArgError, "argument too big");
}
len *= RARRAY_LEN(ary);
- ary2 = ary_new(rb_obj_class(ary), len);
+ ary2 = ary_new(rb_cArray, len);
ARY_SET_LEN(ary2, len);
- ptr = RARRAY_CONST_PTR(ary);
+ ptr = RARRAY_CONST_PTR_TRANSIENT(ary);
t = RARRAY_LEN(ary);
if (0 < t) {
- ary_memcpy(ary2, 0, t, ptr);
- while (t <= len/2) {
- ary_memcpy(ary2, t, t, RARRAY_CONST_PTR(ary2));
+ ary_memcpy(ary2, 0, t, ptr);
+ while (t <= len/2) {
+ ary_memcpy(ary2, t, t, RARRAY_CONST_PTR_TRANSIENT(ary2));
t *= 2;
}
if (t < len) {
- ary_memcpy(ary2, t, len-t, RARRAY_CONST_PTR(ary2));
+ ary_memcpy(ary2, t, len-t, RARRAY_CONST_PTR_TRANSIENT(ary2));
}
}
out:
- OBJ_INFECT(ary2, ary);
-
return ary2;
}
/*
* call-seq:
- * ary.assoc(obj) -> element_ary or nil
+ * array.assoc(obj) -> found_array or nil
*
- * Searches through an array whose elements are also arrays comparing +obj+
- * with the first element of each contained array using <code>obj.==</code>.
+ * Returns the first element in +self+ that is an \Array
+ * whose first element <tt>==</tt> +obj+:
*
- * Returns the first contained array that matches (that is, the first
- * associated array), or +nil+ if no match is found.
+ * a = [{foo: 0}, [2, 4], [4, 5, 6], [4, 5]]
+ * a.assoc(4) # => [4, 5, 6]
*
- * See also Array#rassoc
+ * Returns +nil+ if no such element is found.
*
- * s1 = [ "colors", "red", "blue", "green" ]
- * s2 = [ "letters", "a", "b", "c" ]
- * s3 = "foo"
- * a = [ s1, s2, s3 ]
- * a.assoc("letters") #=> [ "letters", "a", "b", "c" ]
- * a.assoc("foo") #=> nil
+ * Related: #rassoc.
*/
VALUE
@@ -3833,30 +5231,27 @@ rb_ary_assoc(VALUE ary, VALUE key)
VALUE v;
for (i = 0; i < RARRAY_LEN(ary); ++i) {
- v = rb_check_array_type(RARRAY_AREF(ary, i));
- if (!NIL_P(v) && RARRAY_LEN(v) > 0 &&
- rb_equal(RARRAY_AREF(v, 0), key))
- return v;
+ v = rb_check_array_type(RARRAY_AREF(ary, i));
+ if (!NIL_P(v) && RARRAY_LEN(v) > 0 &&
+ rb_equal(RARRAY_AREF(v, 0), key))
+ return v;
}
return Qnil;
}
/*
* call-seq:
- * ary.rassoc(obj) -> element_ary or nil
- *
- * Searches through the array whose elements are also arrays.
+ * array.rassoc(obj) -> found_array or nil
*
- * Compares +obj+ with the second element of each contained array using
- * <code>obj.==</code>.
+ * Returns the first element in +self+ that is an \Array
+ * whose second element <tt>==</tt> +obj+:
*
- * Returns the first contained array that matches +obj+.
+ * a = [{foo: 0}, [2, 4], [4, 5, 6], [4, 5]]
+ * a.rassoc(4) # => [2, 4]
*
- * See also Array#assoc.
+ * Returns +nil+ if no such element is found.
*
- * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
- * a.rassoc("two") #=> [2, "two"]
- * a.rassoc("four") #=> nil
+ * Related: #assoc.
*/
VALUE
@@ -3866,11 +5261,11 @@ rb_ary_rassoc(VALUE ary, VALUE value)
VALUE v;
for (i = 0; i < RARRAY_LEN(ary); ++i) {
- v = RARRAY_AREF(ary, i);
- if (RB_TYPE_P(v, T_ARRAY) &&
- RARRAY_LEN(v) > 1 &&
- rb_equal(RARRAY_AREF(v, 1), value))
- return v;
+ v = RARRAY_AREF(ary, i);
+ if (RB_TYPE_P(v, T_ARRAY) &&
+ RARRAY_LEN(v) > 1 &&
+ rb_equal(RARRAY_AREF(v, 1), value))
+ return v;
}
return Qnil;
}
@@ -3883,43 +5278,48 @@ recursive_equal(VALUE ary1, VALUE ary2, int recur)
if (recur) return Qtrue; /* Subtle! */
+ /* rb_equal() can evacuate ptrs */
p1 = RARRAY_CONST_PTR(ary1);
p2 = RARRAY_CONST_PTR(ary2);
len1 = RARRAY_LEN(ary1);
for (i = 0; i < len1; i++) {
- if (*p1 != *p2) {
- if (rb_equal(*p1, *p2)) {
- len1 = RARRAY_LEN(ary1);
- if (len1 != RARRAY_LEN(ary2))
- return Qfalse;
- if (len1 < i)
- return Qtrue;
- p1 = RARRAY_CONST_PTR(ary1) + i;
- p2 = RARRAY_CONST_PTR(ary2) + i;
- }
- else {
- return Qfalse;
- }
- }
- p1++;
- p2++;
+ if (*p1 != *p2) {
+ if (rb_equal(*p1, *p2)) {
+ len1 = RARRAY_LEN(ary1);
+ if (len1 != RARRAY_LEN(ary2))
+ return Qfalse;
+ if (len1 < i)
+ return Qtrue;
+ p1 = RARRAY_CONST_PTR(ary1) + i;
+ p2 = RARRAY_CONST_PTR(ary2) + i;
+ }
+ else {
+ return Qfalse;
+ }
+ }
+ p1++;
+ p2++;
}
return Qtrue;
}
/*
* call-seq:
- * ary == other_ary -> bool
+ * array == other_array -> true or false
+ *
+ * Returns +true+ if both <tt>array.size == other_array.size</tt>
+ * and for each index +i+ in +array+, <tt>array[i] == other_array[i]</tt>:
*
- * Equality --- Two arrays are equal if they contain the same number of
- * elements and if each element is equal to (according to Object#==) the
- * corresponding element in +other_ary+.
+ * a0 = [:foo, 'bar', 2]
+ * a1 = [:foo, 'bar', 2.0]
+ * a1 == a0 # => true
+ * [] == [] # => true
*
- * [ "a", "c" ] == [ "a", "c", 7 ] #=> false
- * [ "a", "c", 7 ] == [ "a", "c", 7 ] #=> true
- * [ "a", "c", 7 ] == [ "a", "d", "f" ] #=> false
+ * Otherwise, returns +false+.
*
+ * This method is different from method Array#eql?,
+ * which compares elements using <tt>Object#eql?</tt>.
*/
static VALUE
@@ -3927,13 +5327,13 @@ rb_ary_equal(VALUE ary1, VALUE ary2)
{
if (ary1 == ary2) return Qtrue;
if (!RB_TYPE_P(ary2, T_ARRAY)) {
- if (!rb_respond_to(ary2, idTo_ary)) {
- return Qfalse;
- }
- return rb_equal(ary2, ary1);
+ if (!rb_respond_to(ary2, idTo_ary)) {
+ return Qfalse;
+ }
+ return rb_equal(ary2, ary1);
}
if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return Qfalse;
- if (RARRAY_CONST_PTR(ary1) == RARRAY_CONST_PTR(ary2)) return Qtrue;
+ if (RARRAY_CONST_PTR_TRANSIENT(ary1) == RARRAY_CONST_PTR_TRANSIENT(ary2)) return Qtrue;
return rb_exec_recursive_paired(recursive_equal, ary1, ary2, ary2);
}
@@ -3944,18 +5344,27 @@ recursive_eql(VALUE ary1, VALUE ary2, int recur)
if (recur) return Qtrue; /* Subtle! */
for (i=0; i<RARRAY_LEN(ary1); i++) {
- if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
- return Qfalse;
+ if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
+ return Qfalse;
}
return Qtrue;
}
/*
* call-seq:
- * ary.eql?(other) -> true or false
+ * array.eql? other_array -> true or false
+ *
+ * Returns +true+ if +self+ and +other_array+ are the same size,
+ * and if, for each index +i+ in +self+, <tt>self[i].eql? other_array[i]</tt>:
*
- * Returns +true+ if +self+ and +other+ are the same object,
- * or are both arrays with the same content (according to Object#eql?).
+ * a0 = [:foo, 'bar', 2]
+ * a1 = [:foo, 'bar', 2]
+ * a1.eql?(a0) # => true
+ *
+ * Otherwise, returns +false+.
+ *
+ * This method is different from method Array#==,
+ * which compares using method <tt>Object#==</tt>.
*/
static VALUE
@@ -3964,20 +5373,21 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
if (ary1 == ary2) return Qtrue;
if (!RB_TYPE_P(ary2, T_ARRAY)) return Qfalse;
if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return Qfalse;
- if (RARRAY_CONST_PTR(ary1) == RARRAY_CONST_PTR(ary2)) return Qtrue;
+ if (RARRAY_CONST_PTR_TRANSIENT(ary1) == RARRAY_CONST_PTR_TRANSIENT(ary2)) return Qtrue;
return rb_exec_recursive_paired(recursive_eql, ary1, ary2, ary2);
}
/*
* call-seq:
- * ary.hash -> integer
+ * array.hash -> integer
+ *
+ * Returns the integer hash value for +self+.
*
- * Compute a hash-code for this array.
+ * Two arrays with the same content will have the same hash code (and will compare using eql?):
*
- * Two arrays with the same content will have the same hash code (and will
- * compare using #eql?).
+ * [0, 1, 2].hash == [0, 1, 2].hash # => true
+ * [0, 1, 2].hash == [0, 1, 3].hash # => false
*
- * See also Object#hash.
*/
static VALUE
@@ -3990,8 +5400,8 @@ rb_ary_hash(VALUE ary)
h = rb_hash_start(RARRAY_LEN(ary));
h = rb_hash_uint(h, (st_index_t)rb_ary_hash);
for (i=0; i<RARRAY_LEN(ary); i++) {
- n = rb_hash(RARRAY_AREF(ary, i));
- h = rb_hash_uint(h, NUM2LONG(n));
+ n = rb_hash(RARRAY_AREF(ary, i));
+ h = rb_hash_uint(h, NUM2LONG(n));
}
h = rb_hash_end(h);
return ST2FIX(h);
@@ -3999,14 +5409,13 @@ rb_ary_hash(VALUE ary)
/*
* call-seq:
- * ary.include?(object) -> true or false
+ * array.include?(obj) -> true or false
*
- * Returns +true+ if the given +object+ is present in +self+ (that is, if any
- * element <code>==</code> +object+), otherwise returns +false+.
+ * Returns +true+ if for some index +i+ in +self+, <tt>obj == self[i]</tt>;
+ * otherwise +false+:
*
- * a = [ "a", "b", "c" ]
- * a.include?("b") #=> true
- * a.include?("z") #=> false
+ * [0, 1, 2].include?(2) # => true
+ * [0, 1, 2].include?(3) # => false
*/
VALUE
@@ -4016,10 +5425,10 @@ rb_ary_includes(VALUE ary, VALUE item)
VALUE e;
for (i=0; i<RARRAY_LEN(ary); i++) {
- e = RARRAY_AREF(ary, i);
- if (rb_equal(e, item)) {
- return Qtrue;
- }
+ e = RARRAY_AREF(ary, i);
+ if (rb_equal(e, item)) {
+ return Qtrue;
+ }
}
return Qfalse;
}
@@ -4031,10 +5440,10 @@ rb_ary_includes_by_eql(VALUE ary, VALUE item)
VALUE e;
for (i=0; i<RARRAY_LEN(ary); i++) {
- e = RARRAY_AREF(ary, i);
- if (rb_eql(item, e)) {
- return Qtrue;
- }
+ e = RARRAY_AREF(ary, i);
+ if (rb_eql(item, e)) {
+ return Qtrue;
+ }
}
return Qfalse;
}
@@ -4047,45 +5456,46 @@ recursive_cmp(VALUE ary1, VALUE ary2, int recur)
if (recur) return Qundef; /* Subtle! */
len = RARRAY_LEN(ary1);
if (len > RARRAY_LEN(ary2)) {
- len = RARRAY_LEN(ary2);
+ len = RARRAY_LEN(ary2);
}
for (i=0; i<len; i++) {
- VALUE e1 = rb_ary_elt(ary1, i), e2 = rb_ary_elt(ary2, i);
- VALUE v = rb_funcallv(e1, id_cmp, 1, &e2);
- if (v != INT2FIX(0)) {
- return v;
- }
+ VALUE e1 = rb_ary_elt(ary1, i), e2 = rb_ary_elt(ary2, i);
+ VALUE v = rb_funcallv(e1, id_cmp, 1, &e2);
+ if (v != INT2FIX(0)) {
+ return v;
+ }
}
return Qundef;
}
/*
* call-seq:
- * ary <=> other_ary -> -1, 0, +1 or nil
+ * array <=> other_array -> -1, 0, or 1
+ *
+ * Returns -1, 0, or 1 as +self+ is less than, equal to, or greater than +other_array+.
+ * For each index +i+ in +self+, evaluates <tt>result = self[i] <=> other_array[i]</tt>.
*
- * Comparison --- Returns an integer (+-1+, +0+, or <code>+1</code>) if this
- * array is less than, equal to, or greater than +other_ary+.
+ * Returns -1 if any result is -1:
*
- * Each object in each array is compared (using the <=> operator).
+ * [0, 1, 2] <=> [0, 1, 3] # => -1
*
- * Arrays are compared in an "element-wise" manner; the first element of +ary+
- * is compared with the first one of +other_ary+ using the <=> operator, then
- * each of the second elements, etc...
- * As soon as the result of any such comparison is non zero (i.e. the two
- * corresponding elements are not equal), that result is returned for the
- * whole array comparison.
+ * Returns 1 if any result is 1:
*
- * If all the elements are equal, then the result is based on a comparison of
- * the array lengths. Thus, two arrays are "equal" according to Array#<=> if,
- * and only if, they have the same length and the value of each element is
- * equal to the value of the corresponding element in the other array.
+ * [0, 1, 2] <=> [0, 1, 1] # => 1
*
- * +nil+ is returned if the +other_ary+ is not an array or if the comparison
- * of two elements returned +nil+.
+ * When all results are zero:
*
- * [ "a", "a", "c" ] <=> [ "a", "b", "c" ] #=> -1
- * [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ] #=> +1
- * [ 1, 2 ] <=> [ 1, :two ] #=> nil
+ * - Returns -1 if +array+ is smaller than +other_array+:
+ *
+ * [0, 1, 2] <=> [0, 1, 2, 3] # => -1
+ *
+ * - Returns 1 if +array+ is larger than +other_array+:
+ *
+ * [0, 1, 2] <=> [0, 1] # => 1
+ *
+ * - Returns 0 if +array+ and +other_array+ are the same size:
+ *
+ * [0, 1, 2] <=> [0, 1, 2] # => 0
*
*/
@@ -4099,7 +5509,7 @@ rb_ary_cmp(VALUE ary1, VALUE ary2)
if (NIL_P(ary2)) return Qnil;
if (ary1 == ary2) return INT2FIX(0);
v = rb_exec_recursive_paired(recursive_cmp, ary1, ary2, ary2);
- if (v != Qundef) return v;
+ if (!UNDEF_P(v)) return v;
len = RARRAY_LEN(ary1) - RARRAY_LEN(ary2);
if (len == 0) return INT2FIX(0);
if (len > 0) return INT2FIX(1);
@@ -4112,8 +5522,8 @@ ary_add_hash(VALUE hash, VALUE ary)
long i;
for (i=0; i<RARRAY_LEN(ary); i++) {
- VALUE elt = RARRAY_AREF(ary, i);
- rb_hash_add_new_element(hash, elt, elt);
+ VALUE elt = RARRAY_AREF(ary, i);
+ rb_hash_add_new_element(hash, elt, elt);
}
return hash;
}
@@ -4141,8 +5551,8 @@ ary_add_hash_by(VALUE hash, VALUE ary)
long i;
for (i = 0; i < RARRAY_LEN(ary); ++i) {
- VALUE v = rb_ary_elt(ary, i), k = rb_yield(v);
- rb_hash_add_new_element(hash, k, v);
+ VALUE v = rb_ary_elt(ary, i), k = rb_yield(v);
+ rb_hash_add_new_element(hash, k, v);
}
return hash;
}
@@ -4158,31 +5568,30 @@ static inline void
ary_recycle_hash(VALUE hash)
{
assert(RBASIC_CLASS(hash) == 0);
- if (RHASH(hash)->ntbl) {
- st_table *tbl = RHASH(hash)->ntbl;
- st_free_table(tbl);
+ if (RHASH_ST_TABLE_P(hash)) {
+ st_table *tbl = RHASH_ST_TABLE(hash);
+ st_free_table(tbl);
+ RHASH_ST_CLEAR(hash);
}
- rb_gc_force_recycle(hash);
}
/*
* call-seq:
- * ary - other_ary -> new_ary
- *
- * Array Difference
+ * array - other_array -> new_array
*
- * Returns a new array that is a copy of the original array, removing any
- * items that also appear in +other_ary+. The order is preserved from the
- * original array.
+ * Returns a new \Array containing only those elements from +array+
+ * that are not found in \Array +other_array+;
+ * items are compared using <tt>eql?</tt>;
+ * the order from +array+ is preserved:
*
- * It compares elements using their #hash and #eql? methods for efficiency.
+ * [0, 1, 1, 2, 1, 1, 3, 1, 1] - [1] # => [0, 2, 3]
+ * [0, 1, 2, 3] - [3, 0] # => [1, 2]
+ * [0, 1, 2] - [4] # => [0, 1, 2]
*
- * [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
- *
- * If you need set-like behavior, see the library class Set.
+ * Related: Array#difference.
*/
-static VALUE
+VALUE
rb_ary_diff(VALUE ary1, VALUE ary2)
{
VALUE ary3;
@@ -4190,21 +5599,22 @@ rb_ary_diff(VALUE ary1, VALUE ary2)
long i;
ary2 = to_ary(ary2);
+ if (RARRAY_LEN(ary2) == 0) { return ary_make_shared_copy(ary1); }
ary3 = rb_ary_new();
if (RARRAY_LEN(ary1) <= SMALL_ARRAY_LEN || RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
- for (i=0; i<RARRAY_LEN(ary1); i++) {
- VALUE elt = rb_ary_elt(ary1, i);
- if (rb_ary_includes_by_eql(ary2, elt)) continue;
- rb_ary_push(ary3, elt);
- }
- return ary3;
+ for (i=0; i<RARRAY_LEN(ary1); i++) {
+ VALUE elt = rb_ary_elt(ary1, i);
+ if (rb_ary_includes_by_eql(ary2, elt)) continue;
+ rb_ary_push(ary3, elt);
+ }
+ return ary3;
}
hash = ary_make_hash(ary2);
for (i=0; i<RARRAY_LEN(ary1); i++) {
- if (st_lookup(rb_hash_tbl_raw(hash), RARRAY_AREF(ary1, i), 0)) continue;
- rb_ary_push(ary3, rb_ary_elt(ary1, i));
+ if (rb_hash_stlike_lookup(hash, RARRAY_AREF(ary1, i), NULL)) continue;
+ rb_ary_push(ary3, rb_ary_elt(ary1, i));
}
ary_recycle_hash(hash);
return ary3;
@@ -4212,17 +5622,73 @@ rb_ary_diff(VALUE ary1, VALUE ary2)
/*
* call-seq:
- * ary & other_ary -> new_ary
+ * array.difference(*other_arrays) -> new_array
+ *
+ * Returns a new \Array containing only those elements from +self+
+ * that are not found in any of the Arrays +other_arrays+;
+ * items are compared using <tt>eql?</tt>; order from +self+ is preserved:
+ *
+ * [0, 1, 1, 2, 1, 1, 3, 1, 1].difference([1]) # => [0, 2, 3]
+ * [0, 1, 2, 3].difference([3, 0], [1, 3]) # => [2]
+ * [0, 1, 2].difference([4]) # => [0, 1, 2]
+ *
+ * Returns a copy of +self+ if no arguments given.
+ *
+ * Related: Array#-.
+ */
+
+static VALUE
+rb_ary_difference_multi(int argc, VALUE *argv, VALUE ary)
+{
+ VALUE ary_diff;
+ long i, length;
+ volatile VALUE t0;
+ bool *is_hash = ALLOCV_N(bool, t0, argc);
+ ary_diff = rb_ary_new();
+ length = RARRAY_LEN(ary);
+
+ for (i = 0; i < argc; i++) {
+ argv[i] = to_ary(argv[i]);
+ is_hash[i] = (length > SMALL_ARRAY_LEN && RARRAY_LEN(argv[i]) > SMALL_ARRAY_LEN);
+ if (is_hash[i]) argv[i] = ary_make_hash(argv[i]);
+ }
+
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ int j;
+ VALUE elt = rb_ary_elt(ary, i);
+ for (j = 0; j < argc; j++) {
+ if (is_hash[j]) {
+ if (rb_hash_stlike_lookup(argv[j], RARRAY_AREF(ary, i), NULL))
+ break;
+ }
+ else {
+ if (rb_ary_includes_by_eql(argv[j], elt)) break;
+ }
+ }
+ if (j == argc) rb_ary_push(ary_diff, elt);
+ }
+
+ ALLOCV_END(t0);
+
+ return ary_diff;
+}
+
+
+/*
+ * call-seq:
+ * array & other_array -> new_array
*
- * Set Intersection --- Returns a new array containing unique elements common to the
- * two arrays. The order is preserved from the original array.
+ * Returns a new \Array containing each element found in both +array+ and \Array +other_array+;
+ * duplicates are omitted; items are compared using <tt>eql?</tt>:
*
- * It compares elements using their #hash and #eql? methods for efficiency.
+ * [0, 1, 2, 3] & [1, 2] # => [1, 2]
+ * [0, 1, 0, 1] & [0, 1] # => [0, 1]
*
- * [ 1, 1, 3, 5 ] & [ 3, 2, 1 ] #=> [ 1, 3 ]
- * [ 'a', 'b', 'b', 'z' ] & [ 'a', 'b', 'c' ] #=> [ 'a', 'b' ]
+ * Preserves order from +array+:
*
- * See also Array#uniq.
+ * [0, 1, 2] & [3, 2, 1, 0] # => [0, 1, 2]
+ *
+ * Related: Array#intersection.
*/
@@ -4230,7 +5696,6 @@ static VALUE
rb_ary_and(VALUE ary1, VALUE ary2)
{
VALUE hash, ary3, v;
- st_table *table;
st_data_t vv;
long i;
@@ -4239,30 +5704,62 @@ rb_ary_and(VALUE ary1, VALUE ary2)
if (RARRAY_LEN(ary1) == 0 || RARRAY_LEN(ary2) == 0) return ary3;
if (RARRAY_LEN(ary1) <= SMALL_ARRAY_LEN && RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
- for (i=0; i<RARRAY_LEN(ary1); i++) {
- v = RARRAY_AREF(ary1, i);
- if (!rb_ary_includes_by_eql(ary2, v)) continue;
- if (rb_ary_includes_by_eql(ary3, v)) continue;
- rb_ary_push(ary3, v);
- }
- return ary3;
+ for (i=0; i<RARRAY_LEN(ary1); i++) {
+ v = RARRAY_AREF(ary1, i);
+ if (!rb_ary_includes_by_eql(ary2, v)) continue;
+ if (rb_ary_includes_by_eql(ary3, v)) continue;
+ rb_ary_push(ary3, v);
+ }
+ return ary3;
}
hash = ary_make_hash(ary2);
- table = rb_hash_tbl_raw(hash);
for (i=0; i<RARRAY_LEN(ary1); i++) {
- v = RARRAY_AREF(ary1, i);
- vv = (st_data_t)v;
- if (st_delete(table, &vv, 0)) {
- rb_ary_push(ary3, v);
- }
+ v = RARRAY_AREF(ary1, i);
+ vv = (st_data_t)v;
+ if (rb_hash_stlike_delete(hash, &vv, 0)) {
+ rb_ary_push(ary3, v);
+ }
}
ary_recycle_hash(hash);
return ary3;
}
+/*
+ * call-seq:
+ * array.intersection(*other_arrays) -> new_array
+ *
+ * Returns a new \Array containing each element found both in +self+
+ * and in all of the given Arrays +other_arrays+;
+ * duplicates are omitted; items are compared using <tt>eql?</tt>:
+ *
+ * [0, 1, 2, 3].intersection([0, 1, 2], [0, 1, 3]) # => [0, 1]
+ * [0, 0, 1, 1, 2, 3].intersection([0, 1, 2], [0, 1, 3]) # => [0, 1]
+ *
+ * Preserves order from +self+:
+ *
+ * [0, 1, 2].intersection([2, 1, 0]) # => [0, 1, 2]
+ *
+ * Returns a copy of +self+ if no arguments given.
+ *
+ * Related: Array#&.
+ */
+
+static VALUE
+rb_ary_intersection_multi(int argc, VALUE *argv, VALUE ary)
+{
+ VALUE result = rb_ary_dup(ary);
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ result = rb_ary_and(result, argv[i]);
+ }
+
+ return result;
+}
+
static int
ary_hash_orset(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
{
@@ -4271,50 +5768,60 @@ ary_hash_orset(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
return ST_CONTINUE;
}
+static void
+rb_ary_union(VALUE ary_union, VALUE ary)
+{
+ long i;
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ VALUE elt = rb_ary_elt(ary, i);
+ if (rb_ary_includes_by_eql(ary_union, elt)) continue;
+ rb_ary_push(ary_union, elt);
+ }
+}
+
+static void
+rb_ary_union_hash(VALUE hash, VALUE ary2)
+{
+ long i;
+ for (i = 0; i < RARRAY_LEN(ary2); i++) {
+ VALUE elt = RARRAY_AREF(ary2, i);
+ if (!rb_hash_stlike_update(hash, (st_data_t)elt, ary_hash_orset, (st_data_t)elt)) {
+ RB_OBJ_WRITTEN(hash, Qundef, elt);
+ }
+ }
+}
+
/*
* call-seq:
- * ary | other_ary -> new_ary
- *
- * Set Union --- Returns a new array by joining +ary+ with +other_ary+,
- * excluding any duplicates and preserving the order from the given arrays.
+ * array | other_array -> new_array
*
- * It compares elements using their #hash and #eql? methods for efficiency.
+ * Returns the union of +array+ and \Array +other_array+;
+ * duplicates are removed; order is preserved;
+ * items are compared using <tt>eql?</tt>:
*
- * [ "a", "b", "c" ] | [ "c", "d", "a" ] #=> [ "a", "b", "c", "d" ]
- * [ "c", "d", "a" ] | [ "a", "b", "c" ] #=> [ "c", "d", "a", "b" ]
+ * [0, 1] | [2, 3] # => [0, 1, 2, 3]
+ * [0, 1, 1] | [2, 2, 3] # => [0, 1, 2, 3]
+ * [0, 1, 2] | [3, 2, 1, 0] # => [0, 1, 2, 3]
*
- * See also Array#uniq.
+ * Related: Array#union.
*/
static VALUE
rb_ary_or(VALUE ary1, VALUE ary2)
{
VALUE hash, ary3;
- long i;
ary2 = to_ary(ary2);
if (RARRAY_LEN(ary1) + RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
- ary3 = rb_ary_new();
- for (i=0; i<RARRAY_LEN(ary1); i++) {
- VALUE elt = rb_ary_elt(ary1, i);
- if (rb_ary_includes_by_eql(ary3, elt)) continue;
- rb_ary_push(ary3, elt);
- }
- for (i=0; i<RARRAY_LEN(ary2); i++) {
- VALUE elt = rb_ary_elt(ary2, i);
- if (rb_ary_includes_by_eql(ary3, elt)) continue;
- rb_ary_push(ary3, elt);
- }
- return ary3;
+ ary3 = rb_ary_new();
+ rb_ary_union(ary3, ary1);
+ rb_ary_union(ary3, ary2);
+ return ary3;
}
hash = ary_make_hash(ary1);
- for (i=0; i<RARRAY_LEN(ary2); i++) {
- VALUE elt = RARRAY_AREF(ary2, i);
- if (!st_update(RHASH_TBL_RAW(hash), (st_data_t)elt, ary_hash_orset, (st_data_t)elt)) {
- RB_OBJ_WRITTEN(hash, Qundef, elt);
- }
- }
+ rb_ary_union_hash(hash, ary2);
+
ary3 = rb_hash_values(hash);
ary_recycle_hash(hash);
return ary3;
@@ -4322,114 +5829,474 @@ rb_ary_or(VALUE ary1, VALUE ary2)
/*
* call-seq:
- * ary.max -> obj
- * ary.max {|a, b| block} -> obj
- * ary.max(n) -> array
- * ary.max(n) {|a, b| block} -> array
+ * array.union(*other_arrays) -> new_array
*
- * Returns the object in _ary_ with the maximum value. The
- * first form assumes all objects implement <code>Comparable</code>;
- * the second uses the block to return <em>a <=> b</em>.
+ * Returns a new \Array that is the union of +self+ and all given Arrays +other_arrays+;
+ * duplicates are removed; order is preserved; items are compared using <tt>eql?</tt>:
*
- * ary = %w(albatross dog horse)
- * ary.max #=> "horse"
- * ary.max {|a, b| a.length <=> b.length} #=> "albatross"
+ * [0, 1, 2, 3].union([4, 5], [6, 7]) # => [0, 1, 2, 3, 4, 5, 6, 7]
+ * [0, 1, 1].union([2, 1], [3, 1]) # => [0, 1, 2, 3]
+ * [0, 1, 2, 3].union([3, 2], [1, 0]) # => [0, 1, 2, 3]
*
- * If the +n+ argument is given, maximum +n+ elements are returned
- * as an array.
+ * Returns a copy of +self+ if no arguments given.
+ *
+ * Related: Array#|.
+ */
+
+static VALUE
+rb_ary_union_multi(int argc, VALUE *argv, VALUE ary)
+{
+ int i;
+ long sum;
+ VALUE hash, ary_union;
+
+ sum = RARRAY_LEN(ary);
+ for (i = 0; i < argc; i++) {
+ argv[i] = to_ary(argv[i]);
+ sum += RARRAY_LEN(argv[i]);
+ }
+
+ if (sum <= SMALL_ARRAY_LEN) {
+ ary_union = rb_ary_new();
+
+ rb_ary_union(ary_union, ary);
+ for (i = 0; i < argc; i++) rb_ary_union(ary_union, argv[i]);
+
+ return ary_union;
+ }
+
+ hash = ary_make_hash(ary);
+ for (i = 0; i < argc; i++) rb_ary_union_hash(hash, argv[i]);
+
+ ary_union = rb_hash_values(hash);
+ ary_recycle_hash(hash);
+ return ary_union;
+}
+
+/*
+ * call-seq:
+ * ary.intersect?(other_ary) -> true or false
+ *
+ * Returns +true+ if the array and +other_ary+ have at least one element in
+ * common, otherwise returns +false+:
+ *
+ * a = [ 1, 2, 3 ]
+ * b = [ 3, 4, 5 ]
+ * c = [ 5, 6, 7 ]
+ * a.intersect?(b) #=> true
+ * a.intersect?(c) #=> false
+ *
+ */
+
+static VALUE
+rb_ary_intersect_p(VALUE ary1, VALUE ary2)
+{
+ VALUE hash, v, result, shorter, longer;
+ st_data_t vv;
+ long i;
+
+ ary2 = to_ary(ary2);
+ if (RARRAY_LEN(ary1) == 0 || RARRAY_LEN(ary2) == 0) return Qfalse;
+
+ if (RARRAY_LEN(ary1) <= SMALL_ARRAY_LEN && RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
+ for (i=0; i<RARRAY_LEN(ary1); i++) {
+ v = RARRAY_AREF(ary1, i);
+ if (rb_ary_includes_by_eql(ary2, v)) return Qtrue;
+ }
+ return Qfalse;
+ }
+
+ shorter = ary1;
+ longer = ary2;
+ if (RARRAY_LEN(ary1) > RARRAY_LEN(ary2)) {
+ longer = ary1;
+ shorter = ary2;
+ }
+
+ hash = ary_make_hash(shorter);
+ result = Qfalse;
+
+ for (i=0; i<RARRAY_LEN(longer); i++) {
+ v = RARRAY_AREF(longer, i);
+ vv = (st_data_t)v;
+ if (rb_hash_stlike_lookup(hash, vv, 0)) {
+ result = Qtrue;
+ break;
+ }
+ }
+ ary_recycle_hash(hash);
+
+ return result;
+}
+
+static VALUE
+ary_max_generic(VALUE ary, long i, VALUE vmax)
+{
+ RUBY_ASSERT(i > 0 && i < RARRAY_LEN(ary));
+
+ VALUE v;
+ for (; i < RARRAY_LEN(ary); ++i) {
+ v = RARRAY_AREF(ary, i);
+
+ if (rb_cmpint(rb_funcallv(vmax, id_cmp, 1, &v), vmax, v) < 0) {
+ vmax = v;
+ }
+ }
+
+ return vmax;
+}
+
+static VALUE
+ary_max_opt_fixnum(VALUE ary, long i, VALUE vmax)
+{
+ const long n = RARRAY_LEN(ary);
+ RUBY_ASSERT(i > 0 && i < n);
+ RUBY_ASSERT(FIXNUM_P(vmax));
+
+ VALUE v;
+ for (; i < n; ++i) {
+ v = RARRAY_AREF(ary, i);
+
+ if (FIXNUM_P(v)) {
+ if ((long)vmax < (long)v) {
+ vmax = v;
+ }
+ }
+ else {
+ return ary_max_generic(ary, i, vmax);
+ }
+ }
+
+ return vmax;
+}
+
+static VALUE
+ary_max_opt_float(VALUE ary, long i, VALUE vmax)
+{
+ const long n = RARRAY_LEN(ary);
+ RUBY_ASSERT(i > 0 && i < n);
+ RUBY_ASSERT(RB_FLOAT_TYPE_P(vmax));
+
+ VALUE v;
+ for (; i < n; ++i) {
+ v = RARRAY_AREF(ary, i);
+
+ if (RB_FLOAT_TYPE_P(v)) {
+ if (rb_float_cmp(vmax, v) < 0) {
+ vmax = v;
+ }
+ }
+ else {
+ return ary_max_generic(ary, i, vmax);
+ }
+ }
+
+ return vmax;
+}
+
+static VALUE
+ary_max_opt_string(VALUE ary, long i, VALUE vmax)
+{
+ const long n = RARRAY_LEN(ary);
+ RUBY_ASSERT(i > 0 && i < n);
+ RUBY_ASSERT(STRING_P(vmax));
+
+ VALUE v;
+ for (; i < n; ++i) {
+ v = RARRAY_AREF(ary, i);
+
+ if (STRING_P(v)) {
+ if (rb_str_cmp(vmax, v) < 0) {
+ vmax = v;
+ }
+ }
+ else {
+ return ary_max_generic(ary, i, vmax);
+ }
+ }
+
+ return vmax;
+}
+
+/*
+ * call-seq:
+ * array.max -> element
+ * array.max {|a, b| ... } -> element
+ * array.max(n) -> new_array
+ * array.max(n) {|a, b| ... } -> new_array
+ *
+ * Returns one of the following:
+ *
+ * - The maximum-valued element from +self+.
+ * - A new \Array of maximum-valued elements selected from +self+.
+ *
+ * When no block is given, each element in +self+ must respond to method <tt><=></tt>
+ * with an \Integer.
+ *
+ * With no argument and no block, returns the element in +self+
+ * having the maximum value per method <tt><=></tt>:
+ *
+ * [0, 1, 2].max # => 2
+ *
+ * With an argument \Integer +n+ and no block, returns a new \Array with at most +n+ elements,
+ * in descending order per method <tt><=></tt>:
+ *
+ * [0, 1, 2, 3].max(3) # => [3, 2, 1]
+ * [0, 1, 2, 3].max(6) # => [3, 2, 1, 0]
+ *
+ * When a block is given, the block must return an \Integer.
+ *
+ * With a block and no argument, calls the block <tt>self.size-1</tt> times to compare elements;
+ * returns the element having the maximum value per the block:
+ *
+ * ['0', '00', '000'].max {|a, b| a.size <=> b.size } # => "000"
+ *
+ * With an argument +n+ and a block, returns a new \Array with at most +n+ elements,
+ * in descending order per the block:
+ *
+ * ['0', '00', '000'].max(2) {|a, b| a.size <=> b.size } # => ["000", "00"]
*
- * ary = %w[albatross dog horse]
- * ary.max(2) #=> ["horse", "dog"]
- * ary.max(2) {|a, b| a.length <=> b.length } #=> ["albatross", "horse"]
*/
static VALUE
rb_ary_max(int argc, VALUE *argv, VALUE ary)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE result = Qundef, v;
VALUE num;
long i;
- rb_scan_args(argc, argv, "01", &num);
-
- if (!NIL_P(num))
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
return rb_nmin_run(ary, num, 0, 1, 1);
+ const long n = RARRAY_LEN(ary);
if (rb_block_given_p()) {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- v = RARRAY_AREF(ary, i);
- if (result == Qundef || rb_cmpint(rb_yield_values(2, v, result), v, result) > 0) {
- result = v;
- }
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ v = RARRAY_AREF(ary, i);
+ if (UNDEF_P(result) || rb_cmpint(rb_yield_values(2, v, result), v, result) > 0) {
+ result = v;
+ }
+ }
}
- else {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- v = RARRAY_AREF(ary, i);
- if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) > 0) {
- result = v;
- }
- }
- }
- if (result == Qundef) return Qnil;
+ else if (n > 0) {
+ result = RARRAY_AREF(ary, 0);
+ if (n > 1) {
+ if (FIXNUM_P(result) && CMP_OPTIMIZABLE(INTEGER)) {
+ return ary_max_opt_fixnum(ary, 1, result);
+ }
+ else if (STRING_P(result) && CMP_OPTIMIZABLE(STRING)) {
+ return ary_max_opt_string(ary, 1, result);
+ }
+ else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(FLOAT)) {
+ return ary_max_opt_float(ary, 1, result);
+ }
+ else {
+ return ary_max_generic(ary, 1, result);
+ }
+ }
+ }
+ if (UNDEF_P(result)) return Qnil;
return result;
}
+static VALUE
+ary_min_generic(VALUE ary, long i, VALUE vmin)
+{
+ RUBY_ASSERT(i > 0 && i < RARRAY_LEN(ary));
+
+ VALUE v;
+ for (; i < RARRAY_LEN(ary); ++i) {
+ v = RARRAY_AREF(ary, i);
+
+ if (rb_cmpint(rb_funcallv(vmin, id_cmp, 1, &v), vmin, v) > 0) {
+ vmin = v;
+ }
+ }
+
+ return vmin;
+}
+
+static VALUE
+ary_min_opt_fixnum(VALUE ary, long i, VALUE vmin)
+{
+ const long n = RARRAY_LEN(ary);
+ RUBY_ASSERT(i > 0 && i < n);
+ RUBY_ASSERT(FIXNUM_P(vmin));
+
+ VALUE a;
+ for (; i < n; ++i) {
+ a = RARRAY_AREF(ary, i);
+
+ if (FIXNUM_P(a)) {
+ if ((long)vmin > (long)a) {
+ vmin = a;
+ }
+ }
+ else {
+ return ary_min_generic(ary, i, vmin);
+ }
+ }
+
+ return vmin;
+}
+
+static VALUE
+ary_min_opt_float(VALUE ary, long i, VALUE vmin)
+{
+ const long n = RARRAY_LEN(ary);
+ RUBY_ASSERT(i > 0 && i < n);
+ RUBY_ASSERT(RB_FLOAT_TYPE_P(vmin));
+
+ VALUE a;
+ for (; i < n; ++i) {
+ a = RARRAY_AREF(ary, i);
+
+ if (RB_FLOAT_TYPE_P(a)) {
+ if (rb_float_cmp(vmin, a) > 0) {
+ vmin = a;
+ }
+ }
+ else {
+ return ary_min_generic(ary, i, vmin);
+ }
+ }
+
+ return vmin;
+}
+
+static VALUE
+ary_min_opt_string(VALUE ary, long i, VALUE vmin)
+{
+ const long n = RARRAY_LEN(ary);
+ RUBY_ASSERT(i > 0 && i < n);
+ RUBY_ASSERT(STRING_P(vmin));
+
+ VALUE a;
+ for (; i < n; ++i) {
+ a = RARRAY_AREF(ary, i);
+
+ if (STRING_P(a)) {
+ if (rb_str_cmp(vmin, a) > 0) {
+ vmin = a;
+ }
+ }
+ else {
+ return ary_min_generic(ary, i, vmin);
+ }
+ }
+
+ return vmin;
+}
+
/*
* call-seq:
- * ary.min -> obj
- * ary.min {| a,b | block } -> obj
- * ary.min(n) -> array
- * ary.min(n) {| a,b | block } -> array
+ * array.min -> element
+ * array.min { |a, b| ... } -> element
+ * array.min(n) -> new_array
+ * array.min(n) { |a, b| ... } -> new_array
+ *
+ * Returns one of the following:
*
- * Returns the object in _ary_ with the minimum value. The
- * first form assumes all objects implement <code>Comparable</code>;
- * the second uses the block to return <em>a <=> b</em>.
+ * - The minimum-valued element from +self+.
+ * - A new \Array of minimum-valued elements selected from +self+.
*
- * ary = %w(albatross dog horse)
- * ary.min #=> "albatross"
- * ary.min {|a, b| a.length <=> b.length} #=> "dog"
+ * When no block is given, each element in +self+ must respond to method <tt><=></tt>
+ * with an \Integer.
*
- * If the +n+ argument is given, minimum +n+ elements are returned
- * as an array.
+ * With no argument and no block, returns the element in +self+
+ * having the minimum value per method <tt><=></tt>:
+ *
+ * [0, 1, 2].min # => 0
+ *
+ * With \Integer argument +n+ and no block, returns a new \Array with at most +n+ elements,
+ * in ascending order per method <tt><=></tt>:
+ *
+ * [0, 1, 2, 3].min(3) # => [0, 1, 2]
+ * [0, 1, 2, 3].min(6) # => [0, 1, 2, 3]
+ *
+ * When a block is given, the block must return an Integer.
+ *
+ * With a block and no argument, calls the block <tt>self.size-1</tt> times to compare elements;
+ * returns the element having the minimum value per the block:
+ *
+ * ['0', '00', '000'].min { |a, b| a.size <=> b.size } # => "0"
+ *
+ * With an argument +n+ and a block, returns a new \Array with at most +n+ elements,
+ * in ascending order per the block:
+ *
+ * ['0', '00', '000'].min(2) {|a, b| a.size <=> b.size } # => ["0", "00"]
*
- * ary = %w[albatross dog horse]
- * ary.min(2) #=> ["albatross", "dog"]
- * ary.min(2) {|a, b| a.length <=> b.length } #=> ["dog", "horse"]
*/
static VALUE
rb_ary_min(int argc, VALUE *argv, VALUE ary)
{
- struct cmp_opt_data cmp_opt = { 0, 0 };
VALUE result = Qundef, v;
VALUE num;
long i;
- rb_scan_args(argc, argv, "01", &num);
-
- if (!NIL_P(num))
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0]))
return rb_nmin_run(ary, num, 0, 0, 1);
+ const long n = RARRAY_LEN(ary);
if (rb_block_given_p()) {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- v = RARRAY_AREF(ary, i);
- if (result == Qundef || rb_cmpint(rb_yield_values(2, v, result), v, result) < 0) {
- result = v;
- }
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ v = RARRAY_AREF(ary, i);
+ if (UNDEF_P(result) || rb_cmpint(rb_yield_values(2, v, result), v, result) < 0) {
+ result = v;
+ }
+ }
}
- else {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- v = RARRAY_AREF(ary, i);
- if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) < 0) {
- result = v;
- }
- }
- }
- if (result == Qundef) return Qnil;
+ else if (n > 0) {
+ result = RARRAY_AREF(ary, 0);
+ if (n > 1) {
+ if (FIXNUM_P(result) && CMP_OPTIMIZABLE(INTEGER)) {
+ return ary_min_opt_fixnum(ary, 1, result);
+ }
+ else if (STRING_P(result) && CMP_OPTIMIZABLE(STRING)) {
+ return ary_min_opt_string(ary, 1, result);
+ }
+ else if (RB_FLOAT_TYPE_P(result) && CMP_OPTIMIZABLE(FLOAT)) {
+ return ary_min_opt_float(ary, 1, result);
+ }
+ else {
+ return ary_min_generic(ary, 1, result);
+ }
+ }
+ }
+ if (UNDEF_P(result)) return Qnil;
return result;
}
+/*
+ * call-seq:
+ * array.minmax -> [min_val, max_val]
+ * array.minmax {|a, b| ... } -> [min_val, max_val]
+ *
+ * Returns a new 2-element \Array containing the minimum and maximum values
+ * from +self+, either per method <tt><=></tt> or per a given block:.
+ *
+ * When no block is given, each element in +self+ must respond to method <tt><=></tt>
+ * with an \Integer;
+ * returns a new 2-element \Array containing the minimum and maximum values
+ * from +self+, per method <tt><=></tt>:
+ *
+ * [0, 1, 2].minmax # => [0, 2]
+ *
+ * When a block is given, the block must return an \Integer;
+ * the block is called <tt>self.size-1</tt> times to compare elements;
+ * returns a new 2-element \Array containing the minimum and maximum values
+ * from +self+, per the block:
+ *
+ * ['0', '00', '000'].minmax {|a, b| a.size <=> b.size } # => ["0", "000"]
+ *
+ */
+static VALUE
+rb_ary_minmax(VALUE ary)
+{
+ if (rb_block_given_p()) {
+ return rb_call_super(0, NULL);
+ }
+ return rb_assoc_new(rb_ary_min(0, 0, ary), rb_ary_max(0, 0, ary));
+}
+
static int
push_value(st_data_t key, st_data_t val, st_data_t ary)
{
@@ -4439,31 +6306,33 @@ push_value(st_data_t key, st_data_t val, st_data_t ary)
/*
* call-seq:
- * ary.uniq! -> ary or nil
- * ary.uniq! {|item| ...} -> ary or nil
+ * array.uniq! -> self or nil
+ * array.uniq! {|element| ... } -> self or nil
*
- * Removes duplicate elements from +self+.
+ * Removes duplicate elements from +self+, the first occurrence always being retained;
+ * returns +self+ if any elements removed, +nil+ otherwise.
*
- * If a block is given, it will use the return value of the block for
- * comparison.
+ * With no block given, identifies and removes elements using method <tt>eql?</tt>
+ * to compare.
*
- * It compares values using their #hash and #eql? methods for efficiency.
+ * Returns +self+ if any elements removed:
*
- * +self+ is traversed in order, and the first occurrence is kept.
+ * a = [0, 0, 1, 1, 2, 2]
+ * a.uniq! # => [0, 1, 2]
*
- * Returns +nil+ if no changes are made (that is, no duplicates are found).
+ * Returns +nil+ if no elements removed.
*
- * a = [ "a", "a", "b", "b", "c" ]
- * a.uniq! # => ["a", "b", "c"]
+ * With a block given, calls the block for each element;
+ * identifies (using method <tt>eql?</tt>) and removes
+ * elements for which the block returns duplicate values.
*
- * b = [ "a", "b", "c" ]
- * b.uniq! # => nil
+ * Returns +self+ if any elements removed:
*
- * c = [["student","sam"], ["student","george"], ["teacher","matz"]]
- * c.uniq! {|s| s.first} # => [["student", "sam"], ["teacher", "matz"]]
+ * a = ['a', 'aa', 'aaa', 'b', 'bb', 'bbb']
+ * a.uniq! {|element| element.size } # => ['a', 'aa', 'aaa']
*
+ * Returns +nil+ if no elements removed.
*/
-
static VALUE
rb_ary_uniq_bang(VALUE ary)
{
@@ -4474,22 +6343,22 @@ rb_ary_uniq_bang(VALUE ary)
if (RARRAY_LEN(ary) <= 1)
return Qnil;
if (rb_block_given_p())
- hash = ary_make_hash_by(ary);
+ hash = ary_make_hash_by(ary);
else
- hash = ary_make_hash(ary);
+ hash = ary_make_hash(ary);
hash_size = RHASH_SIZE(hash);
if (RARRAY_LEN(ary) == hash_size) {
- return Qnil;
+ return Qnil;
}
rb_ary_modify_check(ary);
ARY_SET_LEN(ary, 0);
if (ARY_SHARED_P(ary) && !ARY_EMBED_P(ary)) {
- rb_ary_unshare(ary);
- FL_SET_EMBED(ary);
+ rb_ary_unshare(ary);
+ FL_SET_EMBED(ary);
}
ary_resize_capa(ary, hash_size);
- st_foreach(rb_hash_tbl_raw(hash), push_value, ary);
+ rb_hash_foreach(hash, push_value, ary);
ary_recycle_hash(hash);
return ary;
@@ -4497,22 +6366,24 @@ rb_ary_uniq_bang(VALUE ary)
/*
* call-seq:
- * ary.uniq -> new_ary
- * ary.uniq {|item| ...} -> new_ary
+ * array.uniq -> new_array
+ * array.uniq {|element| ... } -> new_array
*
- * Returns a new array by removing duplicate values in +self+.
+ * Returns a new \Array containing those elements from +self+ that are not duplicates,
+ * the first occurrence always being retained.
*
- * If a block is given, it will use the return value of the block for comparison.
+ * With no block given, identifies and omits duplicates using method <tt>eql?</tt>
+ * to compare:
*
- * It compares values using their #hash and #eql? methods for efficiency.
+ * a = [0, 0, 1, 1, 2, 2]
+ * a.uniq # => [0, 1, 2]
*
- * +self+ is traversed in order, and the first occurrence is kept.
+ * With a block given, calls the block for each element;
+ * identifies (using method <tt>eql?</tt>) and omits duplicate values,
+ * that is, those elements for which the block returns the same value:
*
- * a = [ "a", "a", "b", "b", "c" ]
- * a.uniq # => ["a", "b", "c"]
- *
- * b = [["student","sam"], ["student","george"], ["teacher","matz"]]
- * b.uniq {|s| s.first} # => [["student", "sam"], ["teacher", "matz"]]
+ * a = ['a', 'aa', 'aaa', 'b', 'bb', 'bbb']
+ * a.uniq {|element| element.size } # => ["a", "aa", "aaa"]
*
*/
@@ -4521,32 +6392,32 @@ rb_ary_uniq(VALUE ary)
{
VALUE hash, uniq;
- if (RARRAY_LEN(ary) <= 1)
- return rb_ary_dup(ary);
- if (rb_block_given_p()) {
- hash = ary_make_hash_by(ary);
- uniq = rb_hash_values(hash);
+ if (RARRAY_LEN(ary) <= 1) {
+ hash = 0;
+ uniq = rb_ary_dup(ary);
+ }
+ else if (rb_block_given_p()) {
+ hash = ary_make_hash_by(ary);
+ uniq = rb_hash_values(hash);
}
else {
- hash = ary_make_hash(ary);
- uniq = rb_hash_values(hash);
+ hash = ary_make_hash(ary);
+ uniq = rb_hash_values(hash);
+ }
+ if (hash) {
+ ary_recycle_hash(hash);
}
- RBASIC_SET_CLASS(uniq, rb_obj_class(ary));
- ary_recycle_hash(hash);
return uniq;
}
/*
* call-seq:
- * ary.compact! -> ary or nil
+ * array.compact! -> self or nil
*
- * Removes +nil+ elements from the array.
+ * Removes all +nil+ elements from +self+.
*
- * Returns +nil+ if no changes were made, otherwise returns the array.
- *
- * [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ]
- * [ "a", "b", "c" ].compact! #=> nil
+ * Returns +self+ if any elements removed, otherwise +nil+.
*/
static VALUE
@@ -4556,16 +6427,16 @@ rb_ary_compact_bang(VALUE ary)
long n;
rb_ary_modify(ary);
- p = t = (VALUE *)RARRAY_CONST_PTR(ary); /* WB: no new reference */
+ p = t = (VALUE *)RARRAY_CONST_PTR_TRANSIENT(ary); /* WB: no new reference */
end = p + RARRAY_LEN(ary);
while (t < end) {
- if (NIL_P(*t)) t++;
- else *p++ = *t++;
+ if (NIL_P(*t)) t++;
+ else *p++ = *t++;
}
- n = p - RARRAY_CONST_PTR(ary);
+ n = p - RARRAY_CONST_PTR_TRANSIENT(ary);
if (RARRAY_LEN(ary) == n) {
- return Qnil;
+ return Qnil;
}
ary_resize_smaller(ary, n);
@@ -4574,12 +6445,12 @@ rb_ary_compact_bang(VALUE ary)
/*
* call-seq:
- * ary.compact -> new_ary
+ * array.compact -> new_array
*
- * Returns a copy of +self+ with all +nil+ elements removed.
+ * Returns a new \Array containing all non-+nil+ elements from +self+:
*
- * [ "a", nil, "b", nil, "c", nil ].compact
- * #=> [ "a", "b", "c" ]
+ * a = [nil, 0, nil, 1, nil, 2, nil]
+ * a.compact # => [0, 1, 2]
*/
static VALUE
@@ -4592,23 +6463,29 @@ rb_ary_compact(VALUE ary)
/*
* call-seq:
- * ary.count -> int
- * ary.count(obj) -> int
- * ary.count {|item| block} -> int
+ * array.count -> an_integer
+ * array.count(obj) -> an_integer
+ * array.count {|element| ... } -> an_integer
+ *
+ * Returns a count of specified elements.
*
- * Returns the number of elements.
+ * With no argument and no block, returns the count of all elements:
*
- * If an argument is given, counts the number of elements which equal +obj+
- * using <code>==</code>.
+ * [0, 1, 2].count # => 3
+ * [].count # => 0
*
- * If a block is given, counts the number of elements for which the block
- * returns a true value.
+ * With argument +obj+, returns the count of elements <tt>==</tt> to +obj+:
*
- * ary = [1, 2, 4, 2]
- * ary.count #=> 4
- * ary.count(2) #=> 2
- * ary.count {|x| x%2 == 0} #=> 3
+ * [0, 1, 2, 0.0].count(0) # => 2
+ * [0, 1, 2].count(3) # => 0
*
+ * With no argument and a block given, calls the block with each element;
+ * returns the count of elements for which the block returns a truthy value:
+ *
+ * [0, 1, 2, 3].count {|element| element > 1} # => 2
+ *
+ * With argument +obj+ and a block given, issues a warning, ignores the block,
+ * and returns the count of elements <tt>==</tt> to +obj+.
*/
static VALUE
@@ -4616,108 +6493,152 @@ rb_ary_count(int argc, VALUE *argv, VALUE ary)
{
long i, n = 0;
- if (argc == 0) {
- VALUE v;
+ if (rb_check_arity(argc, 0, 1) == 0) {
+ VALUE v;
- if (!rb_block_given_p())
- return LONG2NUM(RARRAY_LEN(ary));
+ if (!rb_block_given_p())
+ return LONG2NUM(RARRAY_LEN(ary));
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- v = RARRAY_AREF(ary, i);
- if (RTEST(rb_yield(v))) n++;
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ v = RARRAY_AREF(ary, i);
+ if (RTEST(rb_yield(v))) n++;
+ }
}
else {
- VALUE obj;
+ VALUE obj = argv[0];
- rb_scan_args(argc, argv, "1", &obj);
- if (rb_block_given_p()) {
- rb_warn("given block not used");
- }
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (rb_equal(RARRAY_AREF(ary, i), obj)) n++;
- }
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ if (rb_equal(RARRAY_AREF(ary, i), obj)) n++;
+ }
}
return LONG2NUM(n);
}
static VALUE
-flatten(VALUE ary, int level, int *modified)
+flatten(VALUE ary, int level)
{
- long i = 0;
- VALUE stack, result, tmp, elt;
- st_table *memo;
+ long i;
+ VALUE stack, result, tmp = 0, elt, vmemo;
+ st_table *memo = 0;
st_data_t id;
- stack = ary_new(0, ARY_DEFAULT_SIZE);
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ elt = RARRAY_AREF(ary, i);
+ tmp = rb_check_array_type(elt);
+ if (!NIL_P(tmp)) {
+ break;
+ }
+ }
+ if (i == RARRAY_LEN(ary)) {
+ return ary;
+ }
+
result = ary_new(0, RARRAY_LEN(ary));
- memo = st_init_numtable();
- st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue);
- *modified = 0;
+ ary_memcpy(result, 0, i, RARRAY_CONST_PTR_TRANSIENT(ary));
+ ARY_SET_LEN(result, i);
+
+ stack = ary_new(0, ARY_DEFAULT_SIZE);
+ rb_ary_push(stack, ary);
+ rb_ary_push(stack, LONG2NUM(i + 1));
+
+ if (level < 0) {
+ vmemo = rb_hash_new();
+ RBASIC_CLEAR_CLASS(vmemo);
+ memo = st_init_numtable();
+ rb_hash_st_table_set(vmemo, memo);
+ st_insert(memo, (st_data_t)ary, (st_data_t)Qtrue);
+ st_insert(memo, (st_data_t)tmp, (st_data_t)Qtrue);
+ }
+
+ ary = tmp;
+ i = 0;
while (1) {
- while (i < RARRAY_LEN(ary)) {
- elt = RARRAY_AREF(ary, i++);
- if (level >= 0 && RARRAY_LEN(stack) / 2 >= level) {
- rb_ary_push(result, elt);
- continue;
- }
- tmp = rb_check_array_type(elt);
- if (RBASIC(result)->klass) {
- rb_raise(rb_eRuntimeError, "flatten reentered");
- }
- if (NIL_P(tmp)) {
- rb_ary_push(result, elt);
- }
- else {
- *modified = 1;
- id = (st_data_t)tmp;
- if (st_lookup(memo, id, 0)) {
- st_free_table(memo);
- rb_raise(rb_eArgError, "tried to flatten recursive array");
- }
- st_insert(memo, id, (st_data_t)Qtrue);
- rb_ary_push(stack, ary);
- rb_ary_push(stack, LONG2NUM(i));
- ary = tmp;
- i = 0;
- }
- }
- if (RARRAY_LEN(stack) == 0) {
- break;
- }
- id = (st_data_t)ary;
- st_delete(memo, &id, 0);
- tmp = rb_ary_pop(stack);
- i = NUM2LONG(tmp);
- ary = rb_ary_pop(stack);
- }
-
- st_free_table(memo);
-
- RBASIC_SET_CLASS(result, rb_obj_class(ary));
+ while (i < RARRAY_LEN(ary)) {
+ elt = RARRAY_AREF(ary, i++);
+ if (level >= 0 && RARRAY_LEN(stack) / 2 >= level) {
+ rb_ary_push(result, elt);
+ continue;
+ }
+ tmp = rb_check_array_type(elt);
+ if (RBASIC(result)->klass) {
+ if (memo) {
+ RB_GC_GUARD(vmemo);
+ st_clear(memo);
+ }
+ rb_raise(rb_eRuntimeError, "flatten reentered");
+ }
+ if (NIL_P(tmp)) {
+ rb_ary_push(result, elt);
+ }
+ else {
+ if (memo) {
+ id = (st_data_t)tmp;
+ if (st_is_member(memo, id)) {
+ st_clear(memo);
+ rb_raise(rb_eArgError, "tried to flatten recursive array");
+ }
+ st_insert(memo, id, (st_data_t)Qtrue);
+ }
+ rb_ary_push(stack, ary);
+ rb_ary_push(stack, LONG2NUM(i));
+ ary = tmp;
+ i = 0;
+ }
+ }
+ if (RARRAY_LEN(stack) == 0) {
+ break;
+ }
+ if (memo) {
+ id = (st_data_t)ary;
+ st_delete(memo, &id, 0);
+ }
+ tmp = rb_ary_pop(stack);
+ i = NUM2LONG(tmp);
+ ary = rb_ary_pop(stack);
+ }
+
+ if (memo) {
+ st_clear(memo);
+ }
+
+ RBASIC_SET_CLASS(result, rb_cArray);
return result;
}
/*
* call-seq:
- * ary.flatten! -> ary or nil
- * ary.flatten!(level) -> ary or nil
+ * array.flatten! -> self or nil
+ * array.flatten!(level) -> self or nil
+ *
+ * Replaces each nested \Array in +self+ with the elements from that \Array;
+ * returns +self+ if any changes, +nil+ otherwise.
+ *
+ * With non-negative \Integer argument +level+, flattens recursively through +level+ levels:
*
- * Flattens +self+ in place.
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten!(1) # => [0, 1, [2, 3], 4, 5]
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten!(2) # => [0, 1, 2, 3, 4, 5]
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten!(3) # => [0, 1, 2, 3, 4, 5]
+ * [0, 1, 2].flatten!(1) # => nil
*
- * Returns +nil+ if no modifications were made (i.e., the array contains no
- * subarrays.)
+ * With no argument, a +nil+ argument, or with negative argument +level+, flattens all levels:
*
- * The optional +level+ argument determines the level of recursion to flatten.
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten! # => [0, 1, 2, 3, 4, 5]
+ * [0, 1, 2].flatten! # => nil
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten!(-1) # => [0, 1, 2, 3, 4, 5]
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten!(-2) # => [0, 1, 2, 3, 4, 5]
+ * [0, 1, 2].flatten!(-1) # => nil
*
- * a = [ 1, 2, [3, [4, 5] ] ]
- * a.flatten! #=> [1, 2, 3, 4, 5]
- * a.flatten! #=> nil
- * a #=> [1, 2, 3, 4, 5]
- * a = [ 1, 2, [3, [4, 5] ] ]
- * a.flatten!(1) #=> [1, 2, 3, [4, 5]]
*/
static VALUE
@@ -4726,15 +6647,14 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary)
int mod = 0, level = -1;
VALUE result, lv;
- rb_scan_args(argc, argv, "01", &lv);
+ lv = (rb_check_arity(argc, 0, 1) ? argv[0] : Qnil);
rb_ary_modify_check(ary);
if (!NIL_P(lv)) level = NUM2INT(lv);
if (level == 0) return Qnil;
- result = flatten(ary, level, &mod);
- if (mod == 0) {
- ary_discard(result);
- return Qnil;
+ result = flatten(ary, level);
+ if (result == ary) {
+ return Qnil;
}
if (!(mod = ARY_EMBED_P(result))) rb_obj_freeze(result);
rb_ary_replace(ary, result);
@@ -4745,287 +6665,212 @@ rb_ary_flatten_bang(int argc, VALUE *argv, VALUE ary)
/*
* call-seq:
- * ary.flatten -> new_ary
- * ary.flatten(level) -> new_ary
- *
- * Returns a new array that is a one-dimensional flattening of +self+
- * (recursively).
- *
- * That is, for every element that is an array, extract its elements into
- * the new array.
+ * array.flatten -> new_array
+ * array.flatten(level) -> new_array
+ *
+ * Returns a new \Array that is a recursive flattening of +self+:
+ * - Each non-Array element is unchanged.
+ * - Each \Array is replaced by its individual elements.
+ *
+ * With non-negative \Integer argument +level+, flattens recursively through +level+ levels:
+ *
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten(0) # => [0, [1, [2, 3], 4], 5]
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten(1) # => [0, 1, [2, 3], 4, 5]
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten(2) # => [0, 1, 2, 3, 4, 5]
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten(3) # => [0, 1, 2, 3, 4, 5]
+ *
+ * With no argument, a +nil+ argument, or with negative argument +level+, flattens all levels:
+ *
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten # => [0, 1, 2, 3, 4, 5]
+ * [0, 1, 2].flatten # => [0, 1, 2]
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten(-1) # => [0, 1, 2, 3, 4, 5]
+ * a = [ 0, [ 1, [2, 3], 4 ], 5 ]
+ * a.flatten(-2) # => [0, 1, 2, 3, 4, 5]
+ * [0, 1, 2].flatten(-1) # => [0, 1, 2]
*
- * The optional +level+ argument determines the level of recursion to
- * flatten.
- *
- * s = [ 1, 2, 3 ] #=> [1, 2, 3]
- * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
- * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
- * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- * a = [ 1, 2, [3, [4, 5] ] ]
- * a.flatten(1) #=> [1, 2, 3, [4, 5]]
*/
static VALUE
rb_ary_flatten(int argc, VALUE *argv, VALUE ary)
{
- int mod = 0, level = -1;
- VALUE result, lv;
+ int level = -1;
+ VALUE result;
- rb_scan_args(argc, argv, "01", &lv);
- if (!NIL_P(lv)) level = NUM2INT(lv);
- if (level == 0) return ary_make_shared_copy(ary);
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(argv[0])) {
+ level = NUM2INT(argv[0]);
+ if (level == 0) return ary_make_shared_copy(ary);
+ }
- result = flatten(ary, level, &mod);
- OBJ_INFECT(result, ary);
+ result = flatten(ary, level);
+ if (result == ary) {
+ result = ary_make_shared_copy(ary);
+ }
return result;
}
-#define OPTHASH_GIVEN_P(opts) \
- (argc > 0 && !NIL_P((opts) = rb_check_hash_type(argv[argc-1])) && (--argc, 1))
-static ID id_random;
-
#define RAND_UPTO(max) (long)rb_random_ulong_limited((randgen), (max)-1)
-/*
- * call-seq:
- * ary.shuffle! -> ary
- * ary.shuffle!(random: rng) -> ary
- *
- * Shuffles elements in +self+ in place.
- *
- * a = [ 1, 2, 3 ] #=> [1, 2, 3]
- * a.shuffle! #=> [2, 3, 1]
- * a #=> [2, 3, 1]
- *
- * The optional +rng+ argument will be used as the random number generator.
- *
- * a.shuffle!(random: Random.new(1)) #=> [1, 3, 2]
- */
-
static VALUE
-rb_ary_shuffle_bang(int argc, VALUE *argv, VALUE ary)
+rb_ary_shuffle_bang(rb_execution_context_t *ec, VALUE ary, VALUE randgen)
{
- VALUE opts, randgen = rb_cRandom;
long i, len;
- if (OPTHASH_GIVEN_P(opts)) {
- VALUE rnd;
- ID keyword_ids[1];
-
- keyword_ids[0] = id_random;
- rb_get_kwargs(opts, keyword_ids, 0, 1, &rnd);
- if (rnd != Qundef) {
- randgen = rnd;
- }
- }
- rb_check_arity(argc, 0, 0);
rb_ary_modify(ary);
i = len = RARRAY_LEN(ary);
RARRAY_PTR_USE(ary, ptr, {
- while (i) {
- long j = RAND_UPTO(i);
- VALUE tmp;
- if (len != RARRAY_LEN(ary) || ptr != RARRAY_CONST_PTR(ary)) {
- rb_raise(rb_eRuntimeError, "modified during shuffle");
- }
- tmp = ptr[--i];
- ptr[i] = ptr[j];
- ptr[j] = tmp;
- }
+ while (i) {
+ long j = RAND_UPTO(i);
+ VALUE tmp;
+ if (len != RARRAY_LEN(ary) || ptr != RARRAY_CONST_PTR_TRANSIENT(ary)) {
+ rb_raise(rb_eRuntimeError, "modified during shuffle");
+ }
+ tmp = ptr[--i];
+ ptr[i] = ptr[j];
+ ptr[j] = tmp;
+ }
}); /* WB: no new reference */
return ary;
}
-
-/*
- * call-seq:
- * ary.shuffle -> new_ary
- * ary.shuffle(random: rng) -> new_ary
- *
- * Returns a new array with elements of +self+ shuffled.
- *
- * a = [ 1, 2, 3 ] #=> [1, 2, 3]
- * a.shuffle #=> [2, 3, 1]
- * a #=> [1, 2, 3]
- *
- * The optional +rng+ argument will be used as the random number generator.
- *
- * a.shuffle(random: Random.new(1)) #=> [1, 3, 2]
- */
-
static VALUE
-rb_ary_shuffle(int argc, VALUE *argv, VALUE ary)
+rb_ary_shuffle(rb_execution_context_t *ec, VALUE ary, VALUE randgen)
{
ary = rb_ary_dup(ary);
- rb_ary_shuffle_bang(argc, argv, ary);
+ rb_ary_shuffle_bang(ec, ary, randgen);
return ary;
}
-
-/*
- * call-seq:
- * ary.sample -> obj
- * ary.sample(random: rng) -> obj
- * ary.sample(n) -> new_ary
- * ary.sample(n, random: rng) -> new_ary
- *
- * Choose a random element or +n+ random elements from the array.
- *
- * The elements are chosen by using random and unique indices into the array
- * in order to ensure that an element doesn't repeat itself unless the array
- * already contained duplicate elements.
- *
- * If the array is empty the first form returns +nil+ and the second form
- * returns an empty array.
- *
- * a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
- * a.sample #=> 7
- * a.sample(4) #=> [6, 4, 2, 5]
- *
- * The optional +rng+ argument will be used as the random number generator.
- *
- * a.sample(random: Random.new(1)) #=> 6
- * a.sample(4, random: Random.new(1)) #=> [6, 10, 9, 2]
- */
-
-
static VALUE
-rb_ary_sample(int argc, VALUE *argv, VALUE ary)
+ary_sample(rb_execution_context_t *ec, VALUE ary, VALUE randgen, VALUE nv, VALUE to_array)
{
- VALUE nv, result;
- VALUE opts, randgen = rb_cRandom;
+ VALUE result;
long n, len, i, j, k, idx[10];
long rnds[numberof(idx)];
long memo_threshold;
- if (OPTHASH_GIVEN_P(opts)) {
- VALUE rnd;
- ID keyword_ids[1];
-
- keyword_ids[0] = id_random;
- rb_get_kwargs(opts, keyword_ids, 0, 1, &rnd);
- if (rnd != Qundef) {
- randgen = rnd;
- }
- }
len = RARRAY_LEN(ary);
- if (argc == 0) {
- if (len < 2)
- i = 0;
- else
- i = RAND_UPTO(len);
+ if (!to_array) {
+ if (len < 2)
+ i = 0;
+ else
+ i = RAND_UPTO(len);
- return rb_ary_elt(ary, i);
+ return rb_ary_elt(ary, i);
}
- rb_scan_args(argc, argv, "1", &nv);
n = NUM2LONG(nv);
if (n < 0) rb_raise(rb_eArgError, "negative sample number");
if (n > len) n = len;
if (n <= numberof(idx)) {
- for (i = 0; i < n; ++i) {
- rnds[i] = RAND_UPTO(len - i);
- }
+ for (i = 0; i < n; ++i) {
+ rnds[i] = RAND_UPTO(len - i);
+ }
}
k = len;
len = RARRAY_LEN(ary);
if (len < k && n <= numberof(idx)) {
- for (i = 0; i < n; ++i) {
- if (rnds[i] >= len) return rb_ary_new_capa(0);
- }
+ for (i = 0; i < n; ++i) {
+ if (rnds[i] >= len) return rb_ary_new_capa(0);
+ }
}
if (n > len) n = len;
switch (n) {
case 0:
- return rb_ary_new_capa(0);
+ return rb_ary_new_capa(0);
case 1:
- i = rnds[0];
- return rb_ary_new_from_values(1, &RARRAY_AREF(ary, i));
+ i = rnds[0];
+ return rb_ary_new_from_args(1, RARRAY_AREF(ary, i));
case 2:
- i = rnds[0];
- j = rnds[1];
- if (j >= i) j++;
- return rb_ary_new_from_args(2, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j));
+ i = rnds[0];
+ j = rnds[1];
+ if (j >= i) j++;
+ return rb_ary_new_from_args(2, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j));
case 3:
- i = rnds[0];
- j = rnds[1];
- k = rnds[2];
- {
- long l = j, g = i;
- if (j >= i) l = i, g = ++j;
- if (k >= l && (++k >= g)) ++k;
- }
- return rb_ary_new_from_args(3, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j), RARRAY_AREF(ary, k));
+ i = rnds[0];
+ j = rnds[1];
+ k = rnds[2];
+ {
+ long l = j, g = i;
+ if (j >= i) l = i, g = ++j;
+ if (k >= l && (++k >= g)) ++k;
+ }
+ return rb_ary_new_from_args(3, RARRAY_AREF(ary, i), RARRAY_AREF(ary, j), RARRAY_AREF(ary, k));
}
memo_threshold =
- len < 2560 ? len / 128 :
- len < 5120 ? len / 64 :
- len < 10240 ? len / 32 :
- len / 16;
+ len < 2560 ? len / 128 :
+ len < 5120 ? len / 64 :
+ len < 10240 ? len / 32 :
+ len / 16;
if (n <= numberof(idx)) {
- long sorted[numberof(idx)];
- sorted[0] = idx[0] = rnds[0];
- for (i=1; i<n; i++) {
- k = rnds[i];
- for (j = 0; j < i; ++j) {
- if (k < sorted[j]) break;
- ++k;
- }
- memmove(&sorted[j+1], &sorted[j], sizeof(sorted[0])*(i-j));
- sorted[j] = idx[i] = k;
- }
- result = rb_ary_new_capa(n);
- RARRAY_PTR_USE(result, ptr_result, {
- for (i=0; i<n; i++) {
- ptr_result[i] = RARRAY_AREF(ary, idx[i]);
- }
- });
+ long sorted[numberof(idx)];
+ sorted[0] = idx[0] = rnds[0];
+ for (i=1; i<n; i++) {
+ k = rnds[i];
+ for (j = 0; j < i; ++j) {
+ if (k < sorted[j]) break;
+ ++k;
+ }
+ memmove(&sorted[j+1], &sorted[j], sizeof(sorted[0])*(i-j));
+ sorted[j] = idx[i] = k;
+ }
+ result = rb_ary_new_capa(n);
+ RARRAY_PTR_USE_TRANSIENT(result, ptr_result, {
+ for (i=0; i<n; i++) {
+ ptr_result[i] = RARRAY_AREF(ary, idx[i]);
+ }
+ });
}
else if (n <= memo_threshold / 2) {
- long max_idx = 0;
+ long max_idx = 0;
#undef RUBY_UNTYPED_DATA_WARNING
#define RUBY_UNTYPED_DATA_WARNING 0
- VALUE vmemo = Data_Wrap_Struct(0, 0, st_free_table, 0);
- st_table *memo = st_init_numtable_with_size(n);
- DATA_PTR(vmemo) = memo;
- result = rb_ary_new_capa(n);
- RARRAY_PTR_USE(result, ptr_result, {
- for (i=0; i<n; i++) {
- long r = RAND_UPTO(len-i) + i;
- ptr_result[i] = r;
- if (r > max_idx) max_idx = r;
- }
- len = RARRAY_LEN(ary);
- if (len <= max_idx) n = 0;
- else if (n > len) n = len;
- RARRAY_PTR_USE(ary, ptr_ary, {
- for (i=0; i<n; i++) {
- long j2 = j = ptr_result[i];
- long i2 = i;
- st_data_t value;
- if (st_lookup(memo, (st_data_t)i, &value)) i2 = (long)value;
- if (st_lookup(memo, (st_data_t)j, &value)) j2 = (long)value;
- st_insert(memo, (st_data_t)j, (st_data_t)i2);
- ptr_result[i] = ptr_ary[j2];
- }
- });
- });
- DATA_PTR(vmemo) = 0;
- st_free_table(memo);
+ VALUE vmemo = Data_Wrap_Struct(0, 0, st_free_table, 0);
+ st_table *memo = st_init_numtable_with_size(n);
+ DATA_PTR(vmemo) = memo;
+ result = rb_ary_new_capa(n);
+ RARRAY_PTR_USE(result, ptr_result, {
+ for (i=0; i<n; i++) {
+ long r = RAND_UPTO(len-i) + i;
+ ptr_result[i] = r;
+ if (r > max_idx) max_idx = r;
+ }
+ len = RARRAY_LEN(ary);
+ if (len <= max_idx) n = 0;
+ else if (n > len) n = len;
+ RARRAY_PTR_USE_TRANSIENT(ary, ptr_ary, {
+ for (i=0; i<n; i++) {
+ long j2 = j = ptr_result[i];
+ long i2 = i;
+ st_data_t value;
+ if (st_lookup(memo, (st_data_t)i, &value)) i2 = (long)value;
+ if (st_lookup(memo, (st_data_t)j, &value)) j2 = (long)value;
+ st_insert(memo, (st_data_t)j, (st_data_t)i2);
+ ptr_result[i] = ptr_ary[j2];
+ }
+ });
+ });
+ DATA_PTR(vmemo) = 0;
+ st_free_table(memo);
}
else {
- result = rb_ary_dup(ary);
- RBASIC_CLEAR_CLASS(result);
- RB_GC_GUARD(ary);
- RARRAY_PTR_USE(result, ptr_result, {
- for (i=0; i<n; i++) {
- j = RAND_UPTO(len-i) + i;
- nv = ptr_result[j];
- ptr_result[j] = ptr_result[i];
- ptr_result[i] = nv;
- }
- });
- RBASIC_SET_CLASS_RAW(result, rb_cArray);
+ result = rb_ary_dup(ary);
+ RBASIC_CLEAR_CLASS(result);
+ RB_GC_GUARD(ary);
+ RARRAY_PTR_USE(result, ptr_result, {
+ for (i=0; i<n; i++) {
+ j = RAND_UPTO(len-i) + i;
+ nv = ptr_result[j];
+ ptr_result[j] = ptr_result[i];
+ ptr_result[i] = nv;
+ }
+ });
+ RBASIC_SET_CLASS_RAW(result, rb_cArray);
}
ARY_SET_LEN(result, n);
@@ -5033,15 +6878,21 @@ rb_ary_sample(int argc, VALUE *argv, VALUE ary)
}
static VALUE
+ary_sample0(rb_execution_context_t *ec, VALUE ary)
+{
+ return ary_sample(ec, ary, rb_cRandom, Qfalse, Qfalse);
+}
+
+static VALUE
rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj)
{
long mul;
VALUE n = Qnil;
if (args && (RARRAY_LEN(args) > 0)) {
- n = RARRAY_AREF(args, 0);
+ n = RARRAY_AREF(args, 0);
}
if (RARRAY_LEN(self) == 0) return INT2FIX(0);
- if (n == Qnil) return DBL2NUM(HUGE_VAL);
+ if (NIL_P(n)) return DBL2NUM(HUGE_VAL);
mul = NUM2LONG(n);
if (mul <= 0) return INT2FIX(0);
n = LONG2FIX(mul);
@@ -5050,38 +6901,50 @@ rb_ary_cycle_size(VALUE self, VALUE args, VALUE eobj)
/*
* call-seq:
- * ary.cycle(n=nil) {|obj| block} -> nil
- * ary.cycle(n=nil) -> Enumerator
+ * array.cycle {|element| ... } -> nil
+ * array.cycle(count) {|element| ... } -> nil
+ * array.cycle -> new_enumerator
+ * array.cycle(count) -> new_enumerator
*
- * Calls the given block for each element +n+ times or forever if +nil+ is
- * given.
+ * When called with positive \Integer argument +count+ and a block,
+ * calls the block with each element, then does so again,
+ * until it has done so +count+ times; returns +nil+:
*
- * Does nothing if a non-positive number is given or the array is empty.
+ * output = []
+ * [0, 1].cycle(2) {|element| output.push(element) } # => nil
+ * output # => [0, 1, 0, 1]
*
- * Returns +nil+ if the loop has finished without getting interrupted.
+ * If +count+ is zero or negative, does not call the block:
*
- * If no block is given, an Enumerator is returned instead.
+ * [0, 1].cycle(0) {|element| fail 'Cannot happen' } # => nil
+ * [0, 1].cycle(-1) {|element| fail 'Cannot happen' } # => nil
*
- * a = ["a", "b", "c"]
- * a.cycle {|x| puts x} # print, a, b, c, a, b, c,.. forever.
- * a.cycle(2) {|x| puts x} # print, a, b, c, a, b, c.
+ * When a block is given, and argument is omitted or +nil+, cycles forever:
+ *
+ * # Prints 0 and 1 forever.
+ * [0, 1].cycle {|element| puts element }
+ * [0, 1].cycle(nil) {|element| puts element }
+ *
+ * When no block is given, returns a new \Enumerator:
+ *
+ * [0, 1].cycle(2) # => #<Enumerator: [0, 1]:cycle(2)>
+ * [0, 1].cycle # => # => #<Enumerator: [0, 1]:cycle>
+ * [0, 1].cycle.first(5) # => [0, 1, 0, 1, 0]
*
*/
-
static VALUE
rb_ary_cycle(int argc, VALUE *argv, VALUE ary)
{
long n, i;
- VALUE nv = Qnil;
- rb_scan_args(argc, argv, "01", &nv);
+ rb_check_arity(argc, 0, 1);
RETURN_SIZED_ENUMERATOR(ary, argc, argv, rb_ary_cycle_size);
- if (NIL_P(nv)) {
+ if (argc == 0 || NIL_P(argv[0])) {
n = -1;
}
else {
- n = NUM2LONG(nv);
+ n = NUM2LONG(argv[0]);
if (n <= 0) return Qnil;
}
@@ -5093,9 +6956,6 @@ rb_ary_cycle(int argc, VALUE *argv, VALUE ary)
return Qnil;
}
-#define tmpary(n) rb_ary_tmp_new(n)
-#define tmpary_discard(a) (ary_discard(a), RBASIC_SET_CLASS_RAW(a, rb_cArray))
-
/*
* Build a ruby array of the corresponding values and yield it to the
* associated block.
@@ -5105,11 +6965,9 @@ static int
yield_indexed_values(const VALUE values, const long r, const long *const p)
{
const VALUE result = rb_ary_new2(r);
- VALUE *const result_array = RARRAY_PTR(result);
- const VALUE *const values_array = RARRAY_CONST_PTR(values);
long i;
- for (i = 0; i < r; i++) result_array[i] = values_array[p[i]];
+ for (i = 0; i < r; i++) ARY_SET(result, i, RARRAY_AREF(values, p[i]));
ARY_SET_LEN(result, r);
rb_yield(result);
return !RBASIC(values)->klass;
@@ -5133,52 +6991,52 @@ permute0(const long n, const long r, long *const p, char *const used, const VALU
long i = 0, index = 0;
for (;;) {
- const char *const unused = memchr(&used[i], 0, n-i);
- if (!unused) {
- if (!index) break;
- i = p[--index]; /* pop index */
- used[i++] = 0; /* index unused */
- }
- else {
- i = unused - used;
- p[index] = i;
- used[i] = 1; /* mark index used */
- ++index;
- if (index < r-1) { /* if not done yet */
- p[index] = i = 0;
- continue;
- }
- for (i = 0; i < n; ++i) {
- if (used[i]) continue;
- p[index] = i;
- if (!yield_indexed_values(values, r, p)) {
- rb_raise(rb_eRuntimeError, "permute reentered");
- }
- }
- i = p[--index]; /* pop index */
- used[i] = 0; /* index unused */
- p[index] = ++i;
- }
+ const char *const unused = memchr(&used[i], 0, n-i);
+ if (!unused) {
+ if (!index) break;
+ i = p[--index]; /* pop index */
+ used[i++] = 0; /* index unused */
+ }
+ else {
+ i = unused - used;
+ p[index] = i;
+ used[i] = 1; /* mark index used */
+ ++index;
+ if (index < r-1) { /* if not done yet */
+ p[index] = i = 0;
+ continue;
+ }
+ for (i = 0; i < n; ++i) {
+ if (used[i]) continue;
+ p[index] = i;
+ if (!yield_indexed_values(values, r, p)) {
+ rb_raise(rb_eRuntimeError, "permute reentered");
+ }
+ }
+ i = p[--index]; /* pop index */
+ used[i] = 0; /* index unused */
+ p[index] = ++i;
+ }
}
}
/*
* Returns the product of from, from-1, ..., from - how_many + 1.
- * http://en.wikipedia.org/wiki/Pochhammer_symbol
+ * https://en.wikipedia.org/wiki/Pochhammer_symbol
*/
static VALUE
descending_factorial(long from, long how_many)
{
VALUE cnt;
if (how_many > 0) {
- cnt = LONG2FIX(from);
- while (--how_many > 0) {
- long v = --from;
- cnt = rb_int_mul(cnt, LONG2FIX(v));
- }
+ cnt = LONG2FIX(from);
+ while (--how_many > 0) {
+ long v = --from;
+ cnt = rb_int_mul(cnt, LONG2FIX(v));
+ }
}
else {
- cnt = LONG2FIX(how_many == 0);
+ cnt = LONG2FIX(how_many == 0);
}
return cnt;
}
@@ -5189,18 +7047,18 @@ binomial_coefficient(long comb, long size)
VALUE r;
long i;
if (comb > size-comb) {
- comb = size-comb;
+ comb = size-comb;
}
if (comb < 0) {
- return LONG2FIX(0);
+ return LONG2FIX(0);
}
else if (comb == 0) {
- return LONG2FIX(1);
+ return LONG2FIX(1);
}
r = LONG2FIX(size);
for (i = 1; i < comb; ++i) {
- r = rb_int_mul(r, LONG2FIX(size - i));
- r = rb_int_idiv(r, LONG2FIX(i + 1));
+ r = rb_int_mul(r, LONG2FIX(size - i));
+ r = rb_int_idiv(r, LONG2FIX(i + 1));
}
return r;
}
@@ -5216,66 +7074,118 @@ rb_ary_permutation_size(VALUE ary, VALUE args, VALUE eobj)
/*
* call-seq:
- * ary.permutation {|p| block} -> ary
- * ary.permutation -> Enumerator
- * ary.permutation(n) {|p| block} -> ary
- * ary.permutation(n) -> Enumerator
+ * array.permutation {|element| ... } -> self
+ * array.permutation(n) {|element| ... } -> self
+ * array.permutation -> new_enumerator
+ * array.permutation(n) -> new_enumerator
+ *
+ * When invoked with a block, yield all permutations of elements of +self+; returns +self+.
+ * The order of permutations is indeterminate.
+ *
+ * When a block and an in-range positive \Integer argument +n+ (<tt>0 < n <= self.size</tt>)
+ * are given, calls the block with all +n+-tuple permutations of +self+.
+ *
+ * Example:
+ *
+ * a = [0, 1, 2]
+ * a.permutation(2) {|permutation| p permutation }
+ *
+ * Output:
+ *
+ * [0, 1]
+ * [0, 2]
+ * [1, 0]
+ * [1, 2]
+ * [2, 0]
+ * [2, 1]
+ *
+ * Another example:
+ *
+ * a = [0, 1, 2]
+ * a.permutation(3) {|permutation| p permutation }
+ *
+ * Output:
+ *
+ * [0, 1, 2]
+ * [0, 2, 1]
+ * [1, 0, 2]
+ * [1, 2, 0]
+ * [2, 0, 1]
+ * [2, 1, 0]
+ *
+ * When +n+ is zero, calls the block once with a new empty \Array:
+ *
+ * a = [0, 1, 2]
+ * a.permutation(0) {|permutation| p permutation }
+ *
+ * Output:
+ *
+ * []
+ *
+ * When +n+ is out of range (negative or larger than <tt>self.size</tt>),
+ * does not call the block:
+ *
+ * a = [0, 1, 2]
+ * a.permutation(-1) {|permutation| fail 'Cannot happen' }
+ * a.permutation(4) {|permutation| fail 'Cannot happen' }
+ *
+ * When a block given but no argument,
+ * behaves the same as <tt>a.permutation(a.size)</tt>:
*
- * When invoked with a block, yield all permutations of length +n+ of the
- * elements of the array, then return the array itself.
+ * a = [0, 1, 2]
+ * a.permutation {|permutation| p permutation }
*
- * If +n+ is not specified, yield all permutations of all elements.
+ * Output:
*
- * The implementation makes no guarantees about the order in which the
- * permutations are yielded.
+ * [0, 1, 2]
+ * [0, 2, 1]
+ * [1, 0, 2]
+ * [1, 2, 0]
+ * [2, 0, 1]
+ * [2, 1, 0]
*
- * If no block is given, an Enumerator is returned instead.
+ * Returns a new \Enumerator if no block given:
*
- * Examples:
+ * a = [0, 1, 2]
+ * a.permutation # => #<Enumerator: [0, 1, 2]:permutation>
+ * a.permutation(2) # => #<Enumerator: [0, 1, 2]:permutation(2)>
*
- * a = [1, 2, 3]
- * a.permutation.to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
- * a.permutation(1).to_a #=> [[1],[2],[3]]
- * a.permutation(2).to_a #=> [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]
- * a.permutation(3).to_a #=> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
- * a.permutation(0).to_a #=> [[]] # one permutation of length 0
- * a.permutation(4).to_a #=> [] # no permutations of length 4
*/
static VALUE
rb_ary_permutation(int argc, VALUE *argv, VALUE ary)
{
- VALUE num;
long r, n, i;
n = RARRAY_LEN(ary); /* Array length */
RETURN_SIZED_ENUMERATOR(ary, argc, argv, rb_ary_permutation_size); /* Return enumerator if no block */
- rb_scan_args(argc, argv, "01", &num);
- r = NIL_P(num) ? n : NUM2LONG(num); /* Permutation size from argument */
+ r = n;
+ if (rb_check_arity(argc, 0, 1) && !NIL_P(argv[0]))
+ r = NUM2LONG(argv[0]); /* Permutation size from argument */
if (r < 0 || n < r) {
- /* no permutations: yield nothing */
+ /* no permutations: yield nothing */
}
else if (r == 0) { /* exactly one permutation: the zero-length array */
- rb_yield(rb_ary_new2(0));
+ rb_yield(rb_ary_new2(0));
}
else if (r == 1) { /* this is a special, easy case */
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
+ }
}
else { /* this is the general case */
- volatile VALUE t0;
- long *p = ALLOCV_N(long, t0, r+roomof(n, sizeof(long)));
- char *used = (char*)(p + r);
- VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
- RBASIC_CLEAR_CLASS(ary0);
+ volatile VALUE t0;
+ long *p = ALLOCV_N(long, t0, r+roomof(n, sizeof(long)));
+ char *used = (char*)(p + r);
+ VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
+ RBASIC_CLEAR_CLASS(ary0);
- MEMZERO(used, char, n); /* initialize array */
+ MEMZERO(used, char, n); /* initialize array */
- permute0(n, r, p, used, ary0); /* compute and yield permutations */
- ALLOCV_END(t0);
- RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
+ permute0(n, r, p, used, ary0); /* compute and yield permutations */
+ ALLOCV_END(t0);
+ RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
}
return ary;
}
@@ -5288,16 +7198,16 @@ combinate0(const long len, const long n, long *const stack, const VALUE values)
MEMZERO(stack+1, long, n);
stack[0] = -1;
for (;;) {
- for (lev++; lev < n; lev++) {
- stack[lev+1] = stack[lev]+1;
- }
- if (!yield_indexed_values(values, n, stack+1)) {
- rb_raise(rb_eRuntimeError, "combination reentered");
- }
- do {
- if (lev == 0) return;
- stack[lev--]++;
- } while (stack[lev+1]+n == len+lev+1);
+ for (lev++; lev < n; lev++) {
+ stack[lev+1] = stack[lev]+1;
+ }
+ if (!yield_indexed_values(values, n, stack+1)) {
+ rb_raise(rb_eRuntimeError, "combination reentered");
+ }
+ do {
+ if (lev == 0) return;
+ stack[lev--]++;
+ } while (stack[lev+1]+n == len+lev+1);
}
}
@@ -5312,26 +7222,55 @@ rb_ary_combination_size(VALUE ary, VALUE args, VALUE eobj)
/*
* call-seq:
- * ary.combination(n) {|c| block} -> ary
- * ary.combination(n) -> Enumerator
+ * array.combination(n) {|element| ... } -> self
+ * array.combination(n) -> new_enumerator
+ *
+ * Calls the block, if given, with combinations of elements of +self+;
+ * returns +self+. The order of combinations is indeterminate.
+ *
+ * When a block and an in-range positive \Integer argument +n+ (<tt>0 < n <= self.size</tt>)
+ * are given, calls the block with all +n+-tuple combinations of +self+.
+ *
+ * Example:
+ *
+ * a = [0, 1, 2]
+ * a.combination(2) {|combination| p combination }
+ *
+ * Output:
+ *
+ * [0, 1]
+ * [0, 2]
+ * [1, 2]
+ *
+ * Another example:
+ *
+ * a = [0, 1, 2]
+ * a.combination(3) {|combination| p combination }
+ *
+ * Output:
*
- * When invoked with a block, yields all combinations of length +n+ of elements
- * from the array and then returns the array itself.
+ * [0, 1, 2]
*
- * The implementation makes no guarantees about the order in which the
- * combinations are yielded.
+ * When +n+ is zero, calls the block once with a new empty \Array:
*
- * If no block is given, an Enumerator is returned instead.
+ * a = [0, 1, 2]
+ * a1 = a.combination(0) {|combination| p combination }
*
- * Examples:
+ * Output:
*
- * a = [1, 2, 3, 4]
- * a.combination(1).to_a #=> [[1],[2],[3],[4]]
- * a.combination(2).to_a #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
- * a.combination(3).to_a #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
- * a.combination(4).to_a #=> [[1,2,3,4]]
- * a.combination(0).to_a #=> [[]] # one combination of length 0
- * a.combination(5).to_a #=> [] # no combinations of length 5
+ * []
+ *
+ * When +n+ is out of range (negative or larger than <tt>self.size</tt>),
+ * does not call the block:
+ *
+ * a = [0, 1, 2]
+ * a.combination(-1) {|combination| fail 'Cannot happen' }
+ * a.combination(4) {|combination| fail 'Cannot happen' }
+ *
+ * Returns a new \Enumerator if no block given:
+ *
+ * a = [0, 1, 2]
+ * a.combination(2) # => #<Enumerator: [0, 1, 2]:combination(2)>
*
*/
@@ -5344,25 +7283,25 @@ rb_ary_combination(VALUE ary, VALUE num)
RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_combination_size);
len = RARRAY_LEN(ary);
if (n < 0 || len < n) {
- /* yield nothing */
+ /* yield nothing */
}
else if (n == 0) {
- rb_yield(rb_ary_new2(0));
+ rb_yield(rb_ary_new2(0));
}
else if (n == 1) {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
+ }
}
else {
- VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
- volatile VALUE t0;
- long *stack = ALLOCV_N(long, t0, n+1);
+ VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
+ volatile VALUE t0;
+ long *stack = ALLOCV_N(long, t0, n+1);
- RBASIC_CLEAR_CLASS(ary0);
- combinate0(len, n, stack, ary0);
- ALLOCV_END(t0);
- RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
+ RBASIC_CLEAR_CLASS(ary0);
+ combinate0(len, n, stack, ary0);
+ ALLOCV_END(t0);
+ RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
}
return ary;
}
@@ -5386,19 +7325,19 @@ rpermute0(const long n, const long r, long *const p, const VALUE values)
p[index] = i;
for (;;) {
- if (++index < r-1) {
- p[index] = i = 0;
- continue;
- }
- for (i = 0; i < n; ++i) {
- p[index] = i;
- if (!yield_indexed_values(values, r, p)) {
- rb_raise(rb_eRuntimeError, "repeated permute reentered");
- }
- }
- do {
- if (index <= 0) return;
- } while ((i = ++p[--index]) >= n);
+ if (++index < r-1) {
+ p[index] = i = 0;
+ continue;
+ }
+ for (i = 0; i < n; ++i) {
+ p[index] = i;
+ if (!yield_indexed_values(values, r, p)) {
+ rb_raise(rb_eRuntimeError, "repeated permute reentered");
+ }
+ }
+ do {
+ if (index <= 0) return;
+ } while ((i = ++p[--index]) >= n);
}
}
@@ -5409,37 +7348,79 @@ rb_ary_repeated_permutation_size(VALUE ary, VALUE args, VALUE eobj)
long k = NUM2LONG(RARRAY_AREF(args, 0));
if (k < 0) {
- return LONG2FIX(0);
+ return LONG2FIX(0);
}
if (n <= 0) {
- return LONG2FIX(!k);
+ return LONG2FIX(!k);
}
return rb_int_positive_pow(n, (unsigned long)k);
}
/*
* call-seq:
- * ary.repeated_permutation(n) {|p| block} -> ary
- * ary.repeated_permutation(n) -> Enumerator
+ * array.repeated_permutation(n) {|permutation| ... } -> self
+ * array.repeated_permutation(n) -> new_enumerator
+ *
+ * Calls the block with each repeated permutation of length +n+ of the elements of +self+;
+ * each permutation is an \Array;
+ * returns +self+. The order of the permutations is indeterminate.
+ *
+ * When a block and a positive \Integer argument +n+ are given, calls the block with each
+ * +n+-tuple repeated permutation of the elements of +self+.
+ * The number of permutations is <tt>self.size**n</tt>.
+ *
+ * +n+ = 1:
+ *
+ * a = [0, 1, 2]
+ * a.repeated_permutation(1) {|permutation| p permutation }
*
- * When invoked with a block, yield all repeated permutations of length +n+ of
- * the elements of the array, then return the array itself.
+ * Output:
*
- * The implementation makes no guarantees about the order in which the repeated
- * permutations are yielded.
+ * [0]
+ * [1]
+ * [2]
*
- * If no block is given, an Enumerator is returned instead.
+ * +n+ = 2:
*
- * Examples:
+ * a.repeated_permutation(2) {|permutation| p permutation }
+ *
+ * Output:
+ *
+ * [0, 0]
+ * [0, 1]
+ * [0, 2]
+ * [1, 0]
+ * [1, 1]
+ * [1, 2]
+ * [2, 0]
+ * [2, 1]
+ * [2, 2]
+ *
+ * If +n+ is zero, calls the block once with an empty \Array.
+ *
+ * If +n+ is negative, does not call the block:
+ *
+ * a.repeated_permutation(-1) {|permutation| fail 'Cannot happen' }
+ *
+ * Returns a new \Enumerator if no block given:
+ *
+ * a = [0, 1, 2]
+ * a.repeated_permutation(2) # => #<Enumerator: [0, 1, 2]:permutation(2)>
+ *
+ * Using Enumerators, it's convenient to show the permutations and counts
+ * for some values of +n+:
+ *
+ * e = a.repeated_permutation(0)
+ * e.size # => 1
+ * e.to_a # => [[]]
+ * e = a.repeated_permutation(1)
+ * e.size # => 3
+ * e.to_a # => [[0], [1], [2]]
+ * e = a.repeated_permutation(2)
+ * e.size # => 9
+ * e.to_a # => [[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]
*
- * a = [1, 2]
- * a.repeated_permutation(1).to_a #=> [[1], [2]]
- * a.repeated_permutation(2).to_a #=> [[1,1],[1,2],[2,1],[2,2]]
- * a.repeated_permutation(3).to_a #=> [[1,1,1],[1,1,2],[1,2,1],[1,2,2],
- * # [2,1,1],[2,1,2],[2,2,1],[2,2,2]]
- * a.repeated_permutation(0).to_a #=> [[]] # one permutation of length 0
*/
-
static VALUE
rb_ary_repeated_permutation(VALUE ary, VALUE num)
{
@@ -5450,25 +7431,25 @@ rb_ary_repeated_permutation(VALUE ary, VALUE num)
r = NUM2LONG(num); /* Permutation size from argument */
if (r < 0) {
- /* no permutations: yield nothing */
+ /* no permutations: yield nothing */
}
else if (r == 0) { /* exactly one permutation: the zero-length array */
- rb_yield(rb_ary_new2(0));
+ rb_yield(rb_ary_new2(0));
}
else if (r == 1) { /* this is a special, easy case */
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
+ }
}
else { /* this is the general case */
- volatile VALUE t0;
- long *p = ALLOCV_N(long, t0, r);
- VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
- RBASIC_CLEAR_CLASS(ary0);
+ volatile VALUE t0;
+ long *p = ALLOCV_N(long, t0, r);
+ VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
+ RBASIC_CLEAR_CLASS(ary0);
- rpermute0(n, r, p, ary0); /* compute and yield repeated permutations */
- ALLOCV_END(t0);
- RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
+ rpermute0(n, r, p, ary0); /* compute and yield repeated permutations */
+ ALLOCV_END(t0);
+ RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
}
return ary;
}
@@ -5480,19 +7461,19 @@ rcombinate0(const long n, const long r, long *const p, const long rest, const VA
p[index] = i;
for (;;) {
- if (++index < r-1) {
- p[index] = i;
- continue;
- }
- for (; i < n; ++i) {
- p[index] = i;
- if (!yield_indexed_values(values, r, p)) {
- rb_raise(rb_eRuntimeError, "repeated combination reentered");
- }
- }
- do {
- if (index <= 0) return;
- } while ((i = ++p[--index]) >= n);
+ if (++index < r-1) {
+ p[index] = i;
+ continue;
+ }
+ for (; i < n; ++i) {
+ p[index] = i;
+ if (!yield_indexed_values(values, r, p)) {
+ rb_raise(rb_eRuntimeError, "repeated combination reentered");
+ }
+ }
+ do {
+ if (index <= 0) return;
+ } while ((i = ++p[--index]) >= n);
}
}
@@ -5502,35 +7483,71 @@ rb_ary_repeated_combination_size(VALUE ary, VALUE args, VALUE eobj)
long n = RARRAY_LEN(ary);
long k = NUM2LONG(RARRAY_AREF(args, 0));
if (k == 0) {
- return LONG2FIX(1);
+ return LONG2FIX(1);
}
return binomial_coefficient(k, n + k - 1);
}
/*
* call-seq:
- * ary.repeated_combination(n) {|c| block} -> ary
- * ary.repeated_combination(n) -> Enumerator
+ * array.repeated_combination(n) {|combination| ... } -> self
+ * array.repeated_combination(n) -> new_enumerator
+ *
+ * Calls the block with each repeated combination of length +n+ of the elements of +self+;
+ * each combination is an \Array;
+ * returns +self+. The order of the combinations is indeterminate.
+ *
+ * When a block and a positive \Integer argument +n+ are given, calls the block with each
+ * +n+-tuple repeated combination of the elements of +self+.
+ * The number of combinations is <tt>(n+1)(n+2)/2</tt>.
+ *
+ * +n+ = 1:
+ *
+ * a = [0, 1, 2]
+ * a.repeated_combination(1) {|combination| p combination }
+ *
+ * Output:
+ *
+ * [0]
+ * [1]
+ * [2]
+ *
+ * +n+ = 2:
+ *
+ * a.repeated_combination(2) {|combination| p combination }
+ *
+ * Output:
+ *
+ * [0, 0]
+ * [0, 1]
+ * [0, 2]
+ * [1, 1]
+ * [1, 2]
+ * [2, 2]
+ *
+ * If +n+ is zero, calls the block once with an empty \Array.
+ *
+ * If +n+ is negative, does not call the block:
*
- * When invoked with a block, yields all repeated combinations of length +n+ of
- * elements from the array and then returns the array itself.
+ * a.repeated_combination(-1) {|combination| fail 'Cannot happen' }
*
- * The implementation makes no guarantees about the order in which the repeated
- * combinations are yielded.
+ * Returns a new \Enumerator if no block given:
*
- * If no block is given, an Enumerator is returned instead.
+ * a = [0, 1, 2]
+ * a.repeated_combination(2) # => #<Enumerator: [0, 1, 2]:combination(2)>
*
- * Examples:
+ * Using Enumerators, it's convenient to show the combinations and counts
+ * for some values of +n+:
*
- * a = [1, 2, 3]
- * a.repeated_combination(1).to_a #=> [[1], [2], [3]]
- * a.repeated_combination(2).to_a #=> [[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]]
- * a.repeated_combination(3).to_a #=> [[1,1,1],[1,1,2],[1,1,3],[1,2,2],[1,2,3],
- * # [1,3,3],[2,2,2],[2,2,3],[2,3,3],[3,3,3]]
- * a.repeated_combination(4).to_a #=> [[1,1,1,1],[1,1,1,2],[1,1,1,3],[1,1,2,2],[1,1,2,3],
- * # [1,1,3,3],[1,2,2,2],[1,2,2,3],[1,2,3,3],[1,3,3,3],
- * # [2,2,2,2],[2,2,2,3],[2,2,3,3],[2,3,3,3],[3,3,3,3]]
- * a.repeated_combination(0).to_a #=> [[]] # one combination of length 0
+ * e = a.repeated_combination(0)
+ * e.size # => 1
+ * e.to_a # => [[]]
+ * e = a.repeated_combination(1)
+ * e.size # => 3
+ * e.to_a # => [[0], [1], [2]]
+ * e = a.repeated_combination(2)
+ * e.size # => 6
+ * e.to_a # => [[0, 0], [0, 1], [0, 2], [1, 1], [1, 2], [2, 2]]
*
*/
@@ -5543,58 +7560,97 @@ rb_ary_repeated_combination(VALUE ary, VALUE num)
RETURN_SIZED_ENUMERATOR(ary, 1, &num, rb_ary_repeated_combination_size); /* Return enumerator if no block */
len = RARRAY_LEN(ary);
if (n < 0) {
- /* yield nothing */
+ /* yield nothing */
}
else if (n == 0) {
- rb_yield(rb_ary_new2(0));
+ rb_yield(rb_ary_new2(0));
}
else if (n == 1) {
- for (i = 0; i < RARRAY_LEN(ary); i++) {
- rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
- }
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ rb_yield(rb_ary_new3(1, RARRAY_AREF(ary, i)));
+ }
}
else if (len == 0) {
- /* yield nothing */
+ /* yield nothing */
}
else {
- volatile VALUE t0;
- long *p = ALLOCV_N(long, t0, n);
- VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
- RBASIC_CLEAR_CLASS(ary0);
+ volatile VALUE t0;
+ long *p = ALLOCV_N(long, t0, n);
+ VALUE ary0 = ary_make_shared_copy(ary); /* private defensive copy of ary */
+ RBASIC_CLEAR_CLASS(ary0);
- rcombinate0(len, n, p, n, ary0); /* compute and yield repeated combinations */
- ALLOCV_END(t0);
- RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
+ rcombinate0(len, n, p, n, ary0); /* compute and yield repeated combinations */
+ ALLOCV_END(t0);
+ RBASIC_SET_CLASS_RAW(ary0, rb_cArray);
}
return ary;
}
/*
* call-seq:
- * ary.product(other_ary, ...) -> new_ary
- * ary.product(other_ary, ...) {|p| block} -> ary
+ * array.product(*other_arrays) -> new_array
+ * array.product(*other_arrays) {|combination| ... } -> self
+ *
+ * Computes and returns or yields all combinations of elements from all the Arrays,
+ * including both +self+ and +other_arrays+:
+ *
+ * - The number of combinations is the product of the sizes of all the arrays,
+ * including both +self+ and +other_arrays+.
+ * - The order of the returned combinations is indeterminate.
+ *
+ * When no block is given, returns the combinations as an \Array of Arrays:
+ *
+ * a = [0, 1, 2]
+ * a1 = [3, 4]
+ * a2 = [5, 6]
+ * p = a.product(a1)
+ * p.size # => 6 # a.size * a1.size
+ * p # => [[0, 3], [0, 4], [1, 3], [1, 4], [2, 3], [2, 4]]
+ * p = a.product(a1, a2)
+ * p.size # => 12 # a.size * a1.size * a2.size
+ * p # => [[0, 3, 5], [0, 3, 6], [0, 4, 5], [0, 4, 6], [1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]]
+ *
+ * If any argument is an empty \Array, returns an empty \Array.
+ *
+ * If no argument is given, returns an \Array of 1-element Arrays,
+ * each containing an element of +self+:
+ *
+ * a.product # => [[0], [1], [2]]
+ *
+ * When a block is given, yields each combination as an \Array; returns +self+:
+ *
+ * a.product(a1) {|combination| p combination }
+ *
+ * Output:
+ *
+ * [0, 3]
+ * [0, 4]
+ * [1, 3]
+ * [1, 4]
+ * [2, 3]
+ * [2, 4]
+ *
+ * If any argument is an empty \Array, does not call the block:
*
- * Returns an array of all combinations of elements from all arrays.
+ * a.product(a1, a2, []) {|combination| fail 'Cannot happen' }
*
- * The length of the returned array is the product of the length of +self+ and
- * the argument arrays.
+ * If no argument is given, yields each element of +self+ as a 1-element \Array:
*
- * If given a block, #product will yield all combinations and return +self+
- * instead.
+ * a.product {|combination| p combination }
+ *
+ * Output:
+ *
+ * [0]
+ * [1]
+ * [2]
*
- * [1,2,3].product([4,5]) #=> [[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]]
- * [1,2].product([1,2]) #=> [[1,1],[1,2],[2,1],[2,2]]
- * [1,2].product([3,4],[5,6]) #=> [[1,3,5],[1,3,6],[1,4,5],[1,4,6],
- * # [2,3,5],[2,3,6],[2,4,5],[2,4,6]]
- * [1,2].product() #=> [[1],[2]]
- * [1,2].product([]) #=> []
*/
static VALUE
rb_ary_product(int argc, VALUE *argv, VALUE ary)
{
int n = argc+1; /* How many arrays we're operating on */
- volatile VALUE t0 = tmpary(n);
+ volatile VALUE t0 = rb_ary_hidden_new(n);
volatile VALUE t1 = Qundef;
VALUE *arrays = RARRAY_PTR(t0); /* The arrays we're computing the product of */
int *counters = ALLOCV_N(int, t1, n); /* The current position in each one */
@@ -5615,64 +7671,64 @@ rb_ary_product(int argc, VALUE *argv, VALUE ary)
/* Otherwise, allocate and fill in an array of results */
if (rb_block_given_p()) {
- /* Make defensive copies of arrays; exit if any is empty */
- for (i = 0; i < n; i++) {
- if (RARRAY_LEN(arrays[i]) == 0) goto done;
- arrays[i] = ary_make_shared_copy(arrays[i]);
- }
+ /* Make defensive copies of arrays; exit if any is empty */
+ for (i = 0; i < n; i++) {
+ if (RARRAY_LEN(arrays[i]) == 0) goto done;
+ arrays[i] = ary_make_shared_copy(arrays[i]);
+ }
}
else {
- /* Compute the length of the result array; return [] if any is empty */
- for (i = 0; i < n; i++) {
- long k = RARRAY_LEN(arrays[i]);
- if (k == 0) {
- result = rb_ary_new2(0);
- goto done;
- }
+ /* Compute the length of the result array; return [] if any is empty */
+ for (i = 0; i < n; i++) {
+ long k = RARRAY_LEN(arrays[i]);
+ if (k == 0) {
+ result = rb_ary_new2(0);
+ goto done;
+ }
if (MUL_OVERFLOW_LONG_P(resultlen, k))
- rb_raise(rb_eRangeError, "too big to product");
- resultlen *= k;
- }
- result = rb_ary_new2(resultlen);
+ rb_raise(rb_eRangeError, "too big to product");
+ resultlen *= k;
+ }
+ result = rb_ary_new2(resultlen);
}
for (;;) {
- int m;
- /* fill in one subarray */
- VALUE subarray = rb_ary_new2(n);
- for (j = 0; j < n; j++) {
- rb_ary_push(subarray, rb_ary_entry(arrays[j], counters[j]));
- }
-
- /* put it on the result array */
- if (NIL_P(result)) {
- FL_SET(t0, FL_USER5);
- rb_yield(subarray);
- if (! FL_TEST(t0, FL_USER5)) {
- rb_raise(rb_eRuntimeError, "product reentered");
- }
- else {
- FL_UNSET(t0, FL_USER5);
- }
- }
- else {
- rb_ary_push(result, subarray);
- }
-
- /*
- * Increment the last counter. If it overflows, reset to 0
- * and increment the one before it.
- */
- m = n-1;
- counters[m]++;
- while (counters[m] == RARRAY_LEN(arrays[m])) {
- counters[m] = 0;
- /* If the first counter overflows, we are done */
- if (--m < 0) goto done;
- counters[m]++;
- }
+ int m;
+ /* fill in one subarray */
+ VALUE subarray = rb_ary_new2(n);
+ for (j = 0; j < n; j++) {
+ rb_ary_push(subarray, rb_ary_entry(arrays[j], counters[j]));
+ }
+
+ /* put it on the result array */
+ if (NIL_P(result)) {
+ FL_SET(t0, RARRAY_SHARED_ROOT_FLAG);
+ rb_yield(subarray);
+ if (!FL_TEST(t0, RARRAY_SHARED_ROOT_FLAG)) {
+ rb_raise(rb_eRuntimeError, "product reentered");
+ }
+ else {
+ FL_UNSET(t0, RARRAY_SHARED_ROOT_FLAG);
+ }
+ }
+ else {
+ rb_ary_push(result, subarray);
+ }
+
+ /*
+ * Increment the last counter. If it overflows, reset to 0
+ * and increment the one before it.
+ */
+ m = n-1;
+ counters[m]++;
+ while (counters[m] == RARRAY_LEN(arrays[m])) {
+ counters[m] = 0;
+ /* If the first counter overflows, we are done */
+ if (--m < 0) goto done;
+ counters[m]++;
+ }
}
+
done:
- tmpary_discard(t0);
ALLOCV_END(t1);
return NIL_P(result) ? ary : result;
@@ -5680,16 +7736,19 @@ done:
/*
* call-seq:
- * ary.take(n) -> new_ary
- *
- * Returns first +n+ elements from the array.
+ * array.take(n) -> new_array
*
- * If a negative number is given, raises an ArgumentError.
+ * Returns a new \Array containing the first +n+ element of +self+,
+ * where +n+ is a non-negative \Integer;
+ * does not modify +self+.
*
- * See also Array#drop
+ * Examples:
*
- * a = [1, 2, 3, 4, 5, 0]
- * a.take(3) #=> [1, 2, 3]
+ * a = [0, 1, 2, 3, 4, 5]
+ * a.take(1) # => [0]
+ * a.take(2) # => [0, 1]
+ * a.take(50) # => [0, 1, 2, 3, 4, 5]
+ * a # => [0, 1, 2, 3, 4, 5]
*
*/
@@ -5698,25 +7757,31 @@ rb_ary_take(VALUE obj, VALUE n)
{
long len = NUM2LONG(n);
if (len < 0) {
- rb_raise(rb_eArgError, "attempt to take negative size");
+ rb_raise(rb_eArgError, "attempt to take negative size");
}
return rb_ary_subseq(obj, 0, len);
}
/*
* call-seq:
- * ary.take_while {|obj| block} -> new_ary
- * ary.take_while -> Enumerator
+ * array.take_while {|element| ... } -> new_array
+ * array.take_while -> new_enumerator
*
- * Passes elements to the block until the block returns +nil+ or +false+, then
- * stops iterating and returns an array of all prior elements.
+ * Returns a new \Array containing zero or more leading elements of +self+;
+ * does not modify +self+.
*
- * If no block is given, an Enumerator is returned instead.
+ * With a block given, calls the block with each successive element of +self+;
+ * stops if the block returns +false+ or +nil+;
+ * returns a new \Array containing those elements for which the block returned a truthy value:
*
- * See also Array#drop_while
+ * a = [0, 1, 2, 3, 4, 5]
+ * a.take_while {|element| element < 3 } # => [0, 1, 2]
+ * a.take_while {|element| true } # => [0, 1, 2, 3, 4, 5]
+ * a # => [0, 1, 2, 3, 4, 5]
*
- * a = [1, 2, 3, 4, 5, 0]
- * a.take_while {|i| i < 3} #=> [1, 2]
+ * With no block given, returns a new \Enumerator:
+ *
+ * [0, 1].take_while # => #<Enumerator: [0, 1]:take_while>
*
*/
@@ -5727,24 +7792,25 @@ rb_ary_take_while(VALUE ary)
RETURN_ENUMERATOR(ary, 0, 0);
for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) break;
+ if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) break;
}
return rb_ary_take(ary, LONG2FIX(i));
}
/*
* call-seq:
- * ary.drop(n) -> new_ary
- *
- * Drops first +n+ elements from +ary+ and returns the rest of the elements in
- * an array.
+ * array.drop(n) -> new_array
*
- * If a negative number is given, raises an ArgumentError.
+ * Returns a new \Array containing all but the first +n+ element of +self+,
+ * where +n+ is a non-negative \Integer;
+ * does not modify +self+.
*
- * See also Array#take
+ * Examples:
*
- * a = [1, 2, 3, 4, 5, 0]
- * a.drop(3) #=> [4, 5, 0]
+ * a = [0, 1, 2, 3, 4, 5]
+ * a.drop(0) # => [0, 1, 2, 3, 4, 5]
+ * a.drop(1) # => [1, 2, 3, 4, 5]
+ * a.drop(2) # => [2, 3, 4, 5]
*
*/
@@ -5754,29 +7820,32 @@ rb_ary_drop(VALUE ary, VALUE n)
VALUE result;
long pos = NUM2LONG(n);
if (pos < 0) {
- rb_raise(rb_eArgError, "attempt to drop negative size");
+ rb_raise(rb_eArgError, "attempt to drop negative size");
}
result = rb_ary_subseq(ary, pos, RARRAY_LEN(ary));
- if (result == Qnil) result = rb_ary_new();
+ if (NIL_P(result)) result = rb_ary_new();
return result;
}
/*
* call-seq:
- * ary.drop_while {|obj| block} -> new_ary
- * ary.drop_while -> Enumerator
+ * array.drop_while {|element| ... } -> new_array
+ * array.drop_while -> new_enumerator
+
+ * Returns a new \Array containing zero or more trailing elements of +self+;
+ * does not modify +self+.
*
- * Drops elements up to, but not including, the first element for which the
- * block returns +nil+ or +false+ and returns an array containing the
- * remaining elements.
+ * With a block given, calls the block with each successive element of +self+;
+ * stops if the block returns +false+ or +nil+;
+ * returns a new \Array _omitting_ those elements for which the block returned a truthy value:
*
- * If no block is given, an Enumerator is returned instead.
+ * a = [0, 1, 2, 3, 4, 5]
+ * a.drop_while {|element| element < 3 } # => [3, 4, 5]
*
- * See also Array#take_while
+ * With no block given, returns a new \Enumerator:
*
- * a = [1, 2, 3, 4, 5, 0]
- * a.drop_while {|i| i < 3 } #=> [3, 4, 5, 0]
+ * [0, 1].drop_while # => # => #<Enumerator: [0, 1]:drop_while>
*
*/
@@ -5787,24 +7856,48 @@ rb_ary_drop_while(VALUE ary)
RETURN_ENUMERATOR(ary, 0, 0);
for (i = 0; i < RARRAY_LEN(ary); i++) {
- if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) break;
+ if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) break;
}
return rb_ary_drop(ary, LONG2FIX(i));
}
/*
* call-seq:
- * ary.any? [{|obj| block} ] -> true or false
- * ary.any?(pattern) -> true or false
+ * array.any? -> true or false
+ * array.any? {|element| ... } -> true or false
+ * array.any?(obj) -> true or false
+ *
+ * Returns +true+ if any element of +self+ meets a given criterion.
+ *
+ * With no block given and no argument, returns +true+ if +self+ has any truthy element,
+ * +false+ otherwise:
+ *
+ * [nil, 0, false].any? # => true
+ * [nil, false].any? # => false
+ * [].any? # => false
*
- * See also Enumerable#any?
+ * With a block given and no argument, calls the block with each element in +self+;
+ * returns +true+ if the block returns any truthy value, +false+ otherwise:
+ *
+ * [0, 1, 2].any? {|element| element > 1 } # => true
+ * [0, 1, 2].any? {|element| element > 2 } # => false
+ *
+ * If argument +obj+ is given, returns +true+ if +obj+.<tt>===</tt> any element,
+ * +false+ otherwise:
+ *
+ * ['food', 'drink'].any?(/foo/) # => true
+ * ['food', 'drink'].any?(/bar/) # => false
+ * [].any?(/foo/) # => false
+ * [0, 1, 2].any?(1) # => true
+ * [0, 1, 2].any?(3) # => false
+ *
+ * Related: Enumerable#any?
*/
static VALUE
rb_ary_any_p(int argc, VALUE *argv, VALUE ary)
{
long i, len = RARRAY_LEN(ary);
- const VALUE *ptr = RARRAY_CONST_PTR(ary);
rb_check_arity(argc, 0, 1);
if (!len) return Qfalse;
@@ -5812,35 +7905,234 @@ rb_ary_any_p(int argc, VALUE *argv, VALUE ary)
if (rb_block_given_p()) {
rb_warn("given block not used");
}
- for (i = 0; i < RARRAY_LEN(ary); ++i) {
- if (RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(ary, i)))) return Qtrue;
- }
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(ary, i)))) return Qtrue;
+ }
}
else if (!rb_block_given_p()) {
- for (i = 0; i < len; ++i) if (RTEST(ptr[i])) return Qtrue;
+ for (i = 0; i < len; ++i) {
+ if (RTEST(RARRAY_AREF(ary, i))) return Qtrue;
+ }
}
else {
- for (i = 0; i < RARRAY_LEN(ary); ++i) {
- if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) return Qtrue;
- }
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) return Qtrue;
+ }
}
return Qfalse;
}
/*
- * call-seq:
- * ary.dig(idx, ...) -> object
+ * call-seq:
+ * array.all? -> true or false
+ * array.all? {|element| ... } -> true or false
+ * array.all?(obj) -> true or false
+ *
+ * Returns +true+ if all elements of +self+ meet a given criterion.
+ *
+ * With no block given and no argument, returns +true+ if +self+ contains only truthy elements,
+ * +false+ otherwise:
+ *
+ * [0, 1, :foo].all? # => true
+ * [0, nil, 2].all? # => false
+ * [].all? # => true
*
- * Extracts the nested value specified by the sequence of <i>idx</i>
- * objects by calling +dig+ at each step, returning +nil+ if any
- * intermediate step is +nil+.
+ * With a block given and no argument, calls the block with each element in +self+;
+ * returns +true+ if the block returns only truthy values, +false+ otherwise:
*
- * a = [[1, [2, 3]]]
+ * [0, 1, 2].all? { |element| element < 3 } # => true
+ * [0, 1, 2].all? { |element| element < 2 } # => false
+ *
+ * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> every element, +false+ otherwise:
+ *
+ * ['food', 'fool', 'foot'].all?(/foo/) # => true
+ * ['food', 'drink'].all?(/bar/) # => false
+ * [].all?(/foo/) # => true
+ * [0, 0, 0].all?(0) # => true
+ * [0, 1, 2].all?(1) # => false
+ *
+ * Related: Enumerable#all?
+ */
+
+static VALUE
+rb_ary_all_p(int argc, VALUE *argv, VALUE ary)
+{
+ long i, len = RARRAY_LEN(ary);
+
+ rb_check_arity(argc, 0, 1);
+ if (!len) return Qtrue;
+ if (argc) {
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (!RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(ary, i)))) return Qfalse;
+ }
+ }
+ else if (!rb_block_given_p()) {
+ for (i = 0; i < len; ++i) {
+ if (!RTEST(RARRAY_AREF(ary, i))) return Qfalse;
+ }
+ }
+ else {
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (!RTEST(rb_yield(RARRAY_AREF(ary, i)))) return Qfalse;
+ }
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * array.none? -> true or false
+ * array.none? {|element| ... } -> true or false
+ * array.none?(obj) -> true or false
+ *
+ * Returns +true+ if no element of +self+ meet a given criterion.
+ *
+ * With no block given and no argument, returns +true+ if +self+ has no truthy elements,
+ * +false+ otherwise:
+ *
+ * [nil, false].none? # => true
+ * [nil, 0, false].none? # => false
+ * [].none? # => true
+ *
+ * With a block given and no argument, calls the block with each element in +self+;
+ * returns +true+ if the block returns no truthy value, +false+ otherwise:
+ *
+ * [0, 1, 2].none? {|element| element > 3 } # => true
+ * [0, 1, 2].none? {|element| element > 1 } # => false
+ *
+ * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> no element, +false+ otherwise:
+ *
+ * ['food', 'drink'].none?(/bar/) # => true
+ * ['food', 'drink'].none?(/foo/) # => false
+ * [].none?(/foo/) # => true
+ * [0, 1, 2].none?(3) # => true
+ * [0, 1, 2].none?(1) # => false
+ *
+ * Related: Enumerable#none?
+ */
+
+static VALUE
+rb_ary_none_p(int argc, VALUE *argv, VALUE ary)
+{
+ long i, len = RARRAY_LEN(ary);
+
+ rb_check_arity(argc, 0, 1);
+ if (!len) return Qtrue;
+ if (argc) {
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(ary, i)))) return Qfalse;
+ }
+ }
+ else if (!rb_block_given_p()) {
+ for (i = 0; i < len; ++i) {
+ if (RTEST(RARRAY_AREF(ary, i))) return Qfalse;
+ }
+ }
+ else {
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) return Qfalse;
+ }
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * array.one? -> true or false
+ * array.one? {|element| ... } -> true or false
+ * array.one?(obj) -> true or false
+ *
+ * Returns +true+ if exactly one element of +self+ meets a given criterion.
+ *
+ * With no block given and no argument, returns +true+ if +self+ has exactly one truthy element,
+ * +false+ otherwise:
+ *
+ * [nil, 0].one? # => true
+ * [0, 0].one? # => false
+ * [nil, nil].one? # => false
+ * [].one? # => false
+ *
+ * With a block given and no argument, calls the block with each element in +self+;
+ * returns +true+ if the block a truthy value for exactly one element, +false+ otherwise:
+ *
+ * [0, 1, 2].one? {|element| element > 0 } # => false
+ * [0, 1, 2].one? {|element| element > 1 } # => true
+ * [0, 1, 2].one? {|element| element > 2 } # => false
+ *
+ * If argument +obj+ is given, returns +true+ if <tt>obj.===</tt> exactly one element,
+ * +false+ otherwise:
+ *
+ * [0, 1, 2].one?(0) # => true
+ * [0, 0, 1].one?(0) # => false
+ * [1, 1, 2].one?(0) # => false
+ * ['food', 'drink'].one?(/bar/) # => false
+ * ['food', 'drink'].one?(/foo/) # => true
+ * [].one?(/foo/) # => false
+ *
+ * Related: Enumerable#one?
+ */
+
+static VALUE
+rb_ary_one_p(int argc, VALUE *argv, VALUE ary)
+{
+ long i, len = RARRAY_LEN(ary);
+ VALUE result = Qfalse;
+
+ rb_check_arity(argc, 0, 1);
+ if (!len) return Qfalse;
+ if (argc) {
+ if (rb_block_given_p()) {
+ rb_warn("given block not used");
+ }
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_funcall(argv[0], idEqq, 1, RARRAY_AREF(ary, i)))) {
+ if (result) return Qfalse;
+ result = Qtrue;
+ }
+ }
+ }
+ else if (!rb_block_given_p()) {
+ for (i = 0; i < len; ++i) {
+ if (RTEST(RARRAY_AREF(ary, i))) {
+ if (result) return Qfalse;
+ result = Qtrue;
+ }
+ }
+ }
+ else {
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
+ if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
+ if (result) return Qfalse;
+ result = Qtrue;
+ }
+ }
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.dig(index, *identifiers) -> object
+ *
+ * Finds and returns the object in nested objects
+ * that is specified by +index+ and +identifiers+.
+ * The nested objects may be instances of various classes.
+ * See {Dig Methods}[rdoc-ref:dig_methods.rdoc].
+ *
+ * Examples:
+ *
+ * a = [:foo, [:bar, :baz, [:bat, :bam]]]
+ * a.dig(1) # => [:bar, :baz, [:bat, :bam]]
+ * a.dig(1, 2) # => [:bat, :bam]
+ * a.dig(1, 2, 0) # => :bat
+ * a.dig(1, 2, 3) # => nil
*
- * a.dig(0, 1, 1) #=> 3
- * a.dig(1, 2, 3) #=> nil
- * a.dig(0, 0, 0) #=> TypeError: Integer does not have #dig method
- * [42, {foo: :bar}].dig(1, :foo) #=> :bar
*/
static VALUE
@@ -5858,14 +8150,8 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z)
{
if (n != 0)
v = rb_fix_plus(LONG2FIX(n), v);
- if (r != Qundef) {
- /* r can be an Integer when mathn is loaded */
- if (FIXNUM_P(r))
- v = rb_fix_plus(r, v);
- else if (RB_TYPE_P(r, T_BIGNUM))
- v = rb_big_plus(r, v);
- else
- v = rb_rational_plus(r, v);
+ if (!UNDEF_P(r)) {
+ v = rb_rational_plus(r, v);
}
else if (!n && z) {
v = rb_fix_plus(LONG2FIX(0), v);
@@ -5875,43 +8161,41 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z)
/*
* call-seq:
- * ary.sum(init=0) -> number
- * ary.sum(init=0) {|e| expr } -> number
- *
- * Returns the sum of elements.
- * For example, [e1, e2, e3].sum returns init + e1 + e2 + e3.
+ * array.sum(init = 0) -> object
+ * array.sum(init = 0) {|element| ... } -> object
*
- * If a block is given, the block is applied to each element
- * before addition.
+ * When no block is given, returns the object equivalent to:
*
- * If <i>ary</i> is empty, it returns <i>init</i>.
+ * sum = init
+ * array.each {|element| sum += element }
+ * sum
*
- * [].sum #=> 0
- * [].sum(0.0) #=> 0.0
- * [1, 2, 3].sum #=> 6
- * [3, 5.5].sum #=> 8.5
- * [2.5, 3.0].sum(0.0) {|e| e * e } #=> 15.25
- * [Object.new].sum #=> TypeError
+ * For example, <tt>[e1, e2, e3].sum</tt> returns <tt>init + e1 + e2 + e3</tt>.
*
- * The (arithmetic) mean value of an array can be obtained as follows.
+ * Examples:
*
- * mean = ary.sum(0.0) / ary.length
+ * a = [0, 1, 2, 3]
+ * a.sum # => 6
+ * a.sum(100) # => 106
*
- * This method can be used for non-numeric objects by
- * explicit <i>init</i> argument.
+ * The elements need not be numeric, but must be <tt>+</tt>-compatible
+ * with each other and with +init+:
*
- * ["a", "b", "c"].sum("") #=> "abc"
- * [[1], [[2]], [3]].sum([]) #=> [1, [2], 3]
+ * a = ['abc', 'def', 'ghi']
+ * a.sum('jkl') # => "jklabcdefghi"
*
- * However, Array#join and Array#flatten is faster than Array#sum for
- * array of strings and array of arrays.
+ * When a block is given, it is called with each element
+ * and the block's return value (instead of the element itself) is used as the addend:
*
- * ["a", "b", "c"].join #=> "abc"
- * [[1], [[2]], [3]].flatten(1) #=> [1, [2], 3]
+ * a = ['zero', 1, :two]
+ * s = a.sum('Coerced and concatenated: ') {|element| element.to_s }
+ * s # => "Coerced and concatenated: zero1two"
*
+ * Notes:
*
- * Array#sum method may not respect method redefinition of "+" methods
- * such as Integer#+.
+ * - Array#join and Array#flatten may be faster than Array#sum
+ * for an \Array of Strings or an \Array of Arrays.
+ * - Array#sum method may not respect method redefinition of "+" methods such as Integer#+.
*
*/
@@ -5922,8 +8206,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
long i, n;
int block_given;
- if (rb_scan_args(argc, argv, "01", &v) == 0)
- v = LONG2FIX(0);
+ v = (rb_check_arity(argc, 0, 1) ? argv[0] : LONG2FIX(0));
block_given = rb_block_given_p();
@@ -5943,10 +8226,10 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
n = 0;
}
}
- else if (RB_TYPE_P(e, T_BIGNUM))
+ else if (RB_BIGNUM_TYPE_P(e))
v = rb_big_plus(e, v);
else if (RB_TYPE_P(e, T_RATIONAL)) {
- if (r == Qundef)
+ if (UNDEF_P(r))
r = e;
else
r = rb_rational_plus(r, e);
@@ -5963,15 +8246,15 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
if (RB_FLOAT_TYPE_P(e)) {
/*
* Kahan-Babuska balancing compensated summation algorithm
- * See http://link.springer.com/article/10.1007/s00607-005-0139-x
+ * See https://link.springer.com/article/10.1007/s00607-005-0139-x
*/
double f, c;
+ double x, t;
f = NUM2DBL(v);
c = 0.0;
goto has_float_value;
for (; i < RARRAY_LEN(ary); i++) {
- double x, t;
e = RARRAY_AREF(ary, i);
if (block_given)
e = rb_yield(e);
@@ -5980,7 +8263,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
x = RFLOAT_VALUE(e);
else if (FIXNUM_P(e))
x = FIX2LONG(e);
- else if (RB_TYPE_P(e, T_BIGNUM))
+ else if (RB_BIGNUM_TYPE_P(e))
x = rb_big2dbl(e);
else if (RB_TYPE_P(e, T_RATIONAL))
x = rb_num2dbl(e);
@@ -6026,56 +8309,135 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
return v;
}
+static VALUE
+rb_ary_deconstruct(VALUE ary)
+{
+ return ary;
+}
+
/*
- * Arrays are ordered, integer-indexed collections of any object.
+ * An \Array is an ordered, integer-indexed collection of objects, called _elements_.
+ * Any object (even another array) may be an array element,
+ * and an array can contain objects of different types.
+ *
+ * == \Array Indexes
+ *
+ * \Array indexing starts at 0, as in C or Java.
+ *
+ * A positive index is an offset from the first element:
+ *
+ * - Index 0 indicates the first element.
+ * - Index 1 indicates the second element.
+ * - ...
+ *
+ * A negative index is an offset, backwards, from the end of the array:
+ *
+ * - Index -1 indicates the last element.
+ * - Index -2 indicates the next-to-last element.
+ * - ...
+ *
+ * A non-negative index is <i>in range</i> if and only if it is smaller than
+ * the size of the array. For a 3-element array:
+ *
+ * - Indexes 0 through 2 are in range.
+ * - Index 3 is out of range.
+ *
+ * A negative index is <i>in range</i> if and only if its absolute value is
+ * not larger than the size of the array. For a 3-element array:
+ *
+ * - Indexes -1 through -3 are in range.
+ * - Index -4 is out of range.
+ *
+ * Although the effective index into an array is always an integer,
+ * some methods (both within and outside of class \Array)
+ * accept one or more non-integer arguments that are
+ * {integer-convertible objects}[rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects].
*
- * Array indexing starts at 0, as in C or Java. A negative index is assumed
- * to be relative to the end of the array---that is, an index of -1 indicates
- * the last element of the array, -2 is the next to last element in the
- * array, and so on.
*
* == Creating Arrays
*
- * A new array can be created by using the literal constructor
- * <code>[]</code>. Arrays can contain different types of objects. For
- * example, the array below contains an Integer, a String and a Float:
+ * You can create an \Array object explicitly with:
+ *
+ * - An {array literal}[rdoc-ref:literals.rdoc@Array+Literals]:
+ *
+ * [1, 'one', :one, [2, 'two', :two]]
+ *
+ * - A {%w or %W: string-array Literal}[rdoc-ref:literals.rdoc@25w+and+-25W-3A+String-Array+Literals]:
+ *
+ * %w[foo bar baz] # => ["foo", "bar", "baz"]
+ * %w[1 % *] # => ["1", "%", "*"]
*
- * ary = [1, "two", 3.0] #=> [1, "two", 3.0]
+ * - A {%i pr %I: symbol-array Literal}[rdoc-ref:literals.rdoc@25i+and+-25I-3A+Symbol-Array+Literals]:
*
- * An array can also be created by explicitly calling Array.new with zero, one
- * (the initial size of the Array) or two arguments (the initial size and a
- * default object).
+ * %i[foo bar baz] # => [:foo, :bar, :baz]
+ * %i[1 % *] # => [:"1", :%, :*]
*
- * ary = Array.new #=> []
- * Array.new(3) #=> [nil, nil, nil]
- * Array.new(3, true) #=> [true, true, true]
+ * - \Method Kernel#Array:
*
- * Note that the second argument populates the array with references to the
- * same object. Therefore, it is only recommended in cases when you need to
- * instantiate arrays with natively immutable objects such as Symbols,
- * numbers, true or false.
+ * Array(["a", "b"]) # => ["a", "b"]
+ * Array(1..5) # => [1, 2, 3, 4, 5]
+ * Array(key: :value) # => [[:key, :value]]
+ * Array(nil) # => []
+ * Array(1) # => [1]
+ * Array({:a => "a", :b => "b"}) # => [[:a, "a"], [:b, "b"]]
*
- * To create an array with separate objects a block can be passed instead.
- * This method is safe to use with mutable objects such as hashes, strings or
- * other arrays:
+ * - \Method Array.new:
*
- * Array.new(4) {Hash.new} #=> [{}, {}, {}, {}]
- * Array.new(4) {|i| i.to_s } #=> ["0", "1", "2", "3"]
+ * Array.new # => []
+ * Array.new(3) # => [nil, nil, nil]
+ * Array.new(4) {Hash.new} # => [{}, {}, {}, {}]
+ * Array.new(3, true) # => [true, true, true]
*
- * This is also a quick way to build up multi-dimensional arrays:
+ * Note that the last example above populates the array
+ * with references to the same object.
+ * This is recommended only in cases where that object is a natively immutable object
+ * such as a symbol, a numeric, +nil+, +true+, or +false+.
*
- * empty_table = Array.new(3) {Array.new(3)}
- * #=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
+ * Another way to create an array with various objects, using a block;
+ * this usage is safe for mutable objects such as hashes, strings or
+ * other arrays:
*
- * An array can also be created by using the Array() method, provided by
- * Kernel, which tries to call #to_ary, then #to_a on its argument.
+ * Array.new(4) {|i| i.to_s } # => ["0", "1", "2", "3"]
*
- * Array({:a => "a", :b => "b"}) #=> [[:a, "a"], [:b, "b"]]
+ * Here is a way to create a multi-dimensional array:
+ *
+ * Array.new(3) {Array.new(3)}
+ * # => [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
+ *
+ * A number of Ruby methods, both in the core and in the standard library,
+ * provide instance method +to_a+, which converts an object to an array.
+ *
+ * - ARGF#to_a
+ * - Array#to_a
+ * - Enumerable#to_a
+ * - Hash#to_a
+ * - MatchData#to_a
+ * - NilClass#to_a
+ * - OptionParser#to_a
+ * - Range#to_a
+ * - Set#to_a
+ * - Struct#to_a
+ * - Time#to_a
+ * - Benchmark::Tms#to_a
+ * - CSV::Table#to_a
+ * - Enumerator::Lazy#to_a
+ * - Gem::List#to_a
+ * - Gem::NameTuple#to_a
+ * - Gem::Platform#to_a
+ * - Gem::RequestSet::Lockfile::Tokenizer#to_a
+ * - Gem::SourceList#to_a
+ * - OpenSSL::X509::Extension#to_a
+ * - OpenSSL::X509::Name#to_a
+ * - Racc::ISet#to_a
+ * - Rinda::RingFinger#to_a
+ * - Ripper::Lexer::Elem#to_a
+ * - RubyVM::InstructionSequence#to_a
+ * - YAML::DBM#to_a
*
* == Example Usage
*
* In addition to the methods it mixes in through the Enumerable module, the
- * Array class has proprietary methods for accessing, searching and otherwise
+ * \Array class has proprietary methods for accessing, searching and otherwise
* manipulating arrays.
*
* Some of the more common ones are illustrated below.
@@ -6123,7 +8485,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
*
* arr.drop(3) #=> [4, 5, 6]
*
- * == Obtaining Information about an Array
+ * == Obtaining Information about an \Array
*
* Arrays keep track of their own length at all times. To query an array
* about the number of elements it contains, use #length, #count or #size.
@@ -6161,7 +8523,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
* arr.insert(3, 'orange', 'pear', 'grapefruit')
* #=> [0, 1, 2, "orange", "pear", "grapefruit", "apple", 3, 4, 5, 6]
*
- * == Removing Items from an Array
+ * == Removing Items from an \Array
*
* The method #pop removes the last element in an array and returns it:
*
@@ -6203,9 +8565,9 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
*
* == Iterating over Arrays
*
- * Like all classes that include the Enumerable module, Array has an each
+ * Like all classes that include the Enumerable module, \Array has an each
* method, which defines what elements should be iterated over and how. In
- * case of Array's #each, all elements in the Array instance are yielded to
+ * case of Array's #each, all elements in the \Array instance are yielded to
* the supplied block in sequence.
*
* Note that this operation leaves the array unchanged.
@@ -6231,7 +8593,8 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
* arr.map! {|a| a**2} #=> [1, 4, 9, 16, 25]
* arr #=> [1, 4, 9, 16, 25]
*
- * == Selecting Items from an Array
+ *
+ * == Selecting Items from an \Array
*
* Elements can be selected from an array according to criteria defined in a
* block. The selection can happen in a destructive or a non-destructive
@@ -6262,18 +8625,198 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary)
* arr.keep_if {|a| a < 4} #=> [1, 2, 3]
* arr #=> [1, 2, 3]
*
+ * == What's Here
+ *
+ * First, what's elsewhere. \Class \Array:
+ *
+ * - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
+ * - Includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
+ * which provides dozens of additional methods.
+ *
+ * Here, class \Array provides methods that are useful for:
+ *
+ * - {Creating an Array}[rdoc-ref:Array@Methods+for+Creating+an+Array]
+ * - {Querying}[rdoc-ref:Array@Methods+for+Querying]
+ * - {Comparing}[rdoc-ref:Array@Methods+for+Comparing]
+ * - {Fetching}[rdoc-ref:Array@Methods+for+Fetching]
+ * - {Assigning}[rdoc-ref:Array@Methods+for+Assigning]
+ * - {Deleting}[rdoc-ref:Array@Methods+for+Deleting]
+ * - {Combining}[rdoc-ref:Array@Methods+for+Combining]
+ * - {Iterating}[rdoc-ref:Array@Methods+for+Iterating]
+ * - {Converting}[rdoc-ref:Array@Methods+for+Converting]
+ * - {And more....}[rdoc-ref:Array@Other+Methods]
+ *
+ * === Methods for Creating an \Array
+ *
+ * - ::[]: Returns a new array populated with given objects.
+ * - ::new: Returns a new array.
+ * - ::try_convert: Returns a new array created from a given object.
+ *
+ * === Methods for Querying
+ *
+ * - #length, #size: Returns the count of elements.
+ * - #include?: Returns whether any element <tt>==</tt> a given object.
+ * - #empty?: Returns whether there are no elements.
+ * - #all?: Returns whether all elements meet a given criterion.
+ * - #any?: Returns whether any element meets a given criterion.
+ * - #none?: Returns whether no element <tt>==</tt> a given object.
+ * - #one?: Returns whether exactly one element <tt>==</tt> a given object.
+ * - #count: Returns the count of elements that meet a given criterion.
+ * - #find_index, #index: Returns the index of the first element that meets a given criterion.
+ * - #rindex: Returns the index of the last element that meets a given criterion.
+ * - #hash: Returns the integer hash code.
+ *
+ * === Methods for Comparing
+ *
+ * - #<=>: Returns -1, 0, or 1 * as +self+ is less than, equal to, or
+ * greater than a given object.
+ * - #==: Returns whether each element in +self+ is <tt>==</tt> to the corresponding element
+ * in a given object.
+ * - #eql?: Returns whether each element in +self+ is <tt>eql?</tt> to the corresponding
+ * element in a given object.
+
+ * === Methods for Fetching
+ *
+ * These methods do not modify +self+.
+ *
+ * - #[]: Returns one or more elements.
+ * - #fetch: Returns the element at a given offset.
+ * - #first: Returns one or more leading elements.
+ * - #last: Returns one or more trailing elements.
+ * - #max: Returns one or more maximum-valued elements,
+ * as determined by <tt><=></tt> or a given block.
+ * - #min: Returns one or more minimum-valued elements,
+ * as determined by <tt><=></tt> or a given block.
+ * - #minmax: Returns the minimum-valued and maximum-valued elements,
+ * as determined by <tt><=></tt> or a given block.
+ * - #assoc: Returns the first element that is an array
+ * whose first element <tt>==</tt> a given object.
+ * - #rassoc: Returns the first element that is an array
+ * whose second element <tt>==</tt> a given object.
+ * - #at: Returns the element at a given offset.
+ * - #values_at: Returns the elements at given offsets.
+ * - #dig: Returns the object in nested objects
+ * that is specified by a given index and additional arguments.
+ * - #drop: Returns trailing elements as determined by a given index.
+ * - #take: Returns leading elements as determined by a given index.
+ * - #drop_while: Returns trailing elements as determined by a given block.
+ * - #take_while: Returns leading elements as determined by a given block.
+ * - #slice: Returns consecutive elements as determined by a given argument.
+ * - #sort: Returns all elements in an order determined by <tt><=></tt> or a given block.
+ * - #reverse: Returns all elements in reverse order.
+ * - #compact: Returns an array containing all non-+nil+ elements.
+ * - #select, #filter: Returns an array containing elements selected by a given block.
+ * - #uniq: Returns an array containing non-duplicate elements.
+ * - #rotate: Returns all elements with some rotated from one end to the other.
+ * - #bsearch: Returns an element selected via a binary search
+ * as determined by a given block.
+ * - #bsearch_index: Returns the index of an element selected via a binary search
+ * as determined by a given block.
+ * - #sample: Returns one or more random elements.
+ * - #shuffle: Returns elements in a random order.
+ *
+ * === Methods for Assigning
+ *
+ * These methods add, replace, or reorder elements in +self+.
+ *
+ * - #[]=: Assigns specified elements with a given object.
+ * - #push, #append, #<<: Appends trailing elements.
+ * - #unshift, #prepend: Prepends leading elements.
+ * - #insert: Inserts given objects at a given offset; does not replace elements.
+ * - #concat: Appends all elements from given arrays.
+ * - #fill: Replaces specified elements with specified objects.
+ * - #replace: Replaces the content of +self+ with the content of a given array.
+ * - #reverse!: Replaces +self+ with its elements reversed.
+ * - #rotate!: Replaces +self+ with its elements rotated.
+ * - #shuffle!: Replaces +self+ with its elements in random order.
+ * - #sort!: Replaces +self+ with its elements sorted,
+ * as determined by <tt><=></tt> or a given block.
+ * - #sort_by!: Replaces +self+ with its elements sorted, as determined by a given block.
+ *
+ * === Methods for Deleting
+ *
+ * Each of these methods removes elements from +self+:
+ *
+ * - #pop: Removes and returns the last element.
+ * - #shift: Removes and returns the first element.
+ * - #compact!: Removes all +nil+ elements.
+ * - #delete: Removes elements equal to a given object.
+ * - #delete_at: Removes the element at a given offset.
+ * - #delete_if: Removes elements specified by a given block.
+ * - #keep_if: Removes elements not specified by a given block.
+ * - #reject!: Removes elements specified by a given block.
+ * - #select!, #filter!: Removes elements not specified by a given block.
+ * - #slice!: Removes and returns a sequence of elements.
+ * - #uniq!: Removes duplicates.
+ *
+ * === Methods for Combining
+ *
+ * - #&: Returns an array containing elements found both in +self+ and a given array.
+ * - #intersection: Returns an array containing elements found both in +self+
+ * and in each given array.
+ * - #+: Returns an array containing all elements of +self+ followed by all elements of a given array.
+ * - #-: Returns an array containing all elements of +self+ that are not found in a given array.
+ * - #|: Returns an array containing all elements of +self+ and all elements of a given array,
+ * duplicates removed.
+ * - #union: Returns an array containing all elements of +self+ and all elements of given arrays,
+ * duplicates removed.
+ * - #difference: Returns an array containing all elements of +self+ that are not found
+ * in any of the given arrays..
+ * - #product: Returns or yields all combinations of elements from +self+ and given arrays.
+ *
+ * === Methods for Iterating
+ *
+ * - #each: Passes each element to a given block.
+ * - #reverse_each: Passes each element, in reverse order, to a given block.
+ * - #each_index: Passes each element index to a given block.
+ * - #cycle: Calls a given block with each element, then does so again,
+ * for a specified number of times, or forever.
+ * - #combination: Calls a given block with combinations of elements of +self+;
+ * a combination does not use the same element more than once.
+ * - #permutation: Calls a given block with permutations of elements of +self+;
+ * a permutation does not use the same element more than once.
+ * - #repeated_combination: Calls a given block with combinations of elements of +self+;
+ * a combination may use the same element more than once.
+ * - #repeated_permutation: Calls a given block with permutations of elements of +self+;
+ * a permutation may use the same element more than once.
+ *
+ * === Methods for Converting
+ *
+ * - #map, #collect: Returns an array containing the block return-value for each element.
+ * - #map!, #collect!: Replaces each element with a block return-value.
+ * - #flatten: Returns an array that is a recursive flattening of +self+.
+ * - #flatten!: Replaces each nested array in +self+ with the elements from that array.
+ * - #inspect, #to_s: Returns a new String containing the elements.
+ * - #join: Returns a newsString containing the elements joined by the field separator.
+ * - #to_a: Returns +self+ or a new array containing all elements.
+ * - #to_ary: Returns +self+.
+ * - #to_h: Returns a new hash formed from the elements.
+ * - #transpose: Transposes +self+, which must be an array of arrays.
+ * - #zip: Returns a new array of arrays containing +self+ and given arrays;
+ * follow the link for details.
+ *
+ * === Other Methods
+ *
+ * - #*: Returns one of the following:
+ *
+ * - With integer argument +n+, a new array that is the concatenation
+ * of +n+ copies of +self+.
+ * - With string argument +field_separator+, a new string that is equivalent to
+ * <tt>join(field_separator)</tt>.
+ *
+ * - #abbrev: Returns a hash of unambiguous abbreviations for elements.
+ * - #pack: Packs the elements into a binary sequence.
+ * - #sum: Returns a sum of elements according to either <tt>+</tt> or a given block.
*/
void
Init_Array(void)
{
-#undef rb_intern
-#define rb_intern(str) rb_intern_const(str)
-
rb_cArray = rb_define_class("Array", rb_cObject);
rb_include_module(rb_cArray, rb_mEnumerable);
rb_define_alloc_func(rb_cArray, empty_ary_alloc);
+ rb_define_singleton_method(rb_cArray, "new", rb_ary_s_new, -1);
rb_define_singleton_method(rb_cArray, "[]", rb_ary_s_create, -1);
rb_define_singleton_method(rb_cArray, "try_convert", rb_ary_s_try_convert, 1);
rb_define_method(rb_cArray, "initialize", rb_ary_initialize, -1);
@@ -6296,6 +8839,10 @@ Init_Array(void)
rb_define_method(rb_cArray, "first", rb_ary_first, -1);
rb_define_method(rb_cArray, "last", rb_ary_last, -1);
rb_define_method(rb_cArray, "concat", rb_ary_concat_multi, -1);
+ rb_define_method(rb_cArray, "union", rb_ary_union_multi, -1);
+ rb_define_method(rb_cArray, "difference", rb_ary_difference_multi, -1);
+ rb_define_method(rb_cArray, "intersection", rb_ary_intersection_multi, -1);
+ rb_define_method(rb_cArray, "intersect?", rb_ary_intersect_p, 1);
rb_define_method(rb_cArray, "<<", rb_ary_push, 1);
rb_define_method(rb_cArray, "push", rb_ary_push_m, -1);
rb_define_alias(rb_cArray, "append", "push");
@@ -6308,7 +8855,7 @@ Init_Array(void)
rb_define_method(rb_cArray, "each_index", rb_ary_each_index, 0);
rb_define_method(rb_cArray, "reverse_each", rb_ary_reverse_each, 0);
rb_define_method(rb_cArray, "length", rb_ary_length, 0);
- rb_define_alias(rb_cArray, "size", "length");
+ rb_define_method(rb_cArray, "size", rb_ary_length, 0);
rb_define_method(rb_cArray, "empty?", rb_ary_empty_p, 0);
rb_define_method(rb_cArray, "find_index", rb_ary_index, -1);
rb_define_method(rb_cArray, "index", rb_ary_index, -1);
@@ -6359,6 +8906,7 @@ Init_Array(void)
rb_define_method(rb_cArray, "max", rb_ary_max, -1);
rb_define_method(rb_cArray, "min", rb_ary_min, -1);
+ rb_define_method(rb_cArray, "minmax", rb_ary_minmax, 0);
rb_define_method(rb_cArray, "uniq", rb_ary_uniq, 0);
rb_define_method(rb_cArray, "uniq!", rb_ary_uniq_bang, 0);
@@ -6367,9 +8915,6 @@ Init_Array(void)
rb_define_method(rb_cArray, "flatten", rb_ary_flatten, -1);
rb_define_method(rb_cArray, "flatten!", rb_ary_flatten_bang, -1);
rb_define_method(rb_cArray, "count", rb_ary_count, -1);
- rb_define_method(rb_cArray, "shuffle!", rb_ary_shuffle_bang, -1);
- rb_define_method(rb_cArray, "shuffle", rb_ary_shuffle, -1);
- rb_define_method(rb_cArray, "sample", rb_ary_sample, -1);
rb_define_method(rb_cArray, "cycle", rb_ary_cycle, -1);
rb_define_method(rb_cArray, "permutation", rb_ary_permutation, -1);
rb_define_method(rb_cArray, "combination", rb_ary_combination, 1);
@@ -6384,8 +8929,13 @@ Init_Array(void)
rb_define_method(rb_cArray, "bsearch", rb_ary_bsearch, 0);
rb_define_method(rb_cArray, "bsearch_index", rb_ary_bsearch_index, 0);
rb_define_method(rb_cArray, "any?", rb_ary_any_p, -1);
+ rb_define_method(rb_cArray, "all?", rb_ary_all_p, -1);
+ rb_define_method(rb_cArray, "none?", rb_ary_none_p, -1);
+ rb_define_method(rb_cArray, "one?", rb_ary_one_p, -1);
rb_define_method(rb_cArray, "dig", rb_ary_dig, -1);
rb_define_method(rb_cArray, "sum", rb_ary_sum, -1);
- id_random = rb_intern("random");
+ rb_define_method(rb_cArray, "deconstruct", rb_ary_deconstruct, 0);
}
+
+#include "array.rbinc"
diff --git a/array.rb b/array.rb
new file mode 100644
index 0000000000..b9fa9844e6
--- /dev/null
+++ b/array.rb
@@ -0,0 +1,69 @@
+class Array
+ # call-seq:
+ # array.shuffle!(random: Random) -> array
+ #
+ # Shuffles the elements of +self+ in place.
+ # a = [1, 2, 3] #=> [1, 2, 3]
+ # a.shuffle! #=> [2, 3, 1]
+ # a #=> [2, 3, 1]
+ #
+ # The optional +random+ argument will be used as the random number generator:
+ # a.shuffle!(random: Random.new(1)) #=> [1, 3, 2]
+ def shuffle!(random: Random)
+ Primitive.rb_ary_shuffle_bang(random)
+ end
+
+ # call-seq:
+ # array.shuffle(random: Random) -> new_ary
+ #
+ # Returns a new array with elements of +self+ shuffled.
+ # a = [1, 2, 3] #=> [1, 2, 3]
+ # a.shuffle #=> [2, 3, 1]
+ # a #=> [1, 2, 3]
+ #
+ # The optional +random+ argument will be used as the random number generator:
+ # a.shuffle(random: Random.new(1)) #=> [1, 3, 2]
+ def shuffle(random: Random)
+ Primitive.rb_ary_shuffle(random)
+ end
+
+ # call-seq:
+ # array.sample(random: Random) -> object
+ # array.sample(n, random: Random) -> new_ary
+ #
+ # Returns random elements from +self+.
+ #
+ # When no arguments are given, returns a random element from +self+:
+ # a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ # a.sample # => 3
+ # a.sample # => 8
+ # If +self+ is empty, returns +nil+.
+ #
+ # When argument +n+ is given, returns a new \Array containing +n+ random
+ # elements from +self+:
+ # a.sample(3) # => [8, 9, 2]
+ # a.sample(6) # => [9, 6, 10, 3, 1, 4]
+ # Returns no more than <tt>a.size</tt> elements
+ # (because no new duplicates are introduced):
+ # a.sample(a.size * 2) # => [6, 4, 1, 8, 5, 9, 10, 2, 3, 7]
+ # But +self+ may contain duplicates:
+ # a = [1, 1, 1, 2, 2, 3]
+ # a.sample(a.size * 2) # => [1, 1, 3, 2, 1, 2]
+ # The argument +n+ must be a non-negative numeric value.
+ # The order of the result array is unrelated to the order of +self+.
+ # Returns a new empty \Array if +self+ is empty.
+ #
+ # The optional +random+ argument will be used as the random number generator:
+ # a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ # a.sample(random: Random.new(1)) #=> 6
+ # a.sample(4, random: Random.new(1)) #=> [6, 10, 9, 2]
+ def sample(n = (ary = false), random: Random)
+ if Primitive.mandatory_only?
+ # Primitive.cexpr! %{ rb_ary_sample(self, rb_cRandom, Qfalse, Qfalse) }
+ Primitive.ary_sample0
+ else
+ # Primitive.cexpr! %{ rb_ary_sample(self, random, n, ary) }
+ Primitive.ary_sample(random, n, ary)
+ end
+ end
+end
diff --git a/ast.c b/ast.c
index 22870f18df..adb7287ed3 100644
--- a/ast.c
+++ b/ast.c
@@ -1,17 +1,23 @@
/* indent-tabs-mode: nil */
+#include "internal.h"
+#include "internal/parse.h"
+#include "internal/symbol.h"
+#include "internal/warnings.h"
+#include "iseq.h"
+#include "node.h"
#include "ruby.h"
#include "ruby/encoding.h"
#include "ruby/util.h"
-#include "internal.h"
-#include "node.h"
#include "vm_core.h"
+#include "builtin.h"
+
static VALUE rb_mAST;
static VALUE rb_cNode;
struct ASTNodeData {
rb_ast_t *ast;
- NODE *node;
+ const NODE *node;
};
static void
@@ -21,9 +27,16 @@ node_gc_mark(void *ptr)
rb_gc_mark((VALUE)data->ast);
}
+static size_t
+node_memsize(const void *ptr)
+{
+ struct ASTNodeData *data = (struct ASTNodeData *)ptr;
+ return rb_ast_memsize(data->ast);
+}
+
static const rb_data_type_t rb_node_type = {
"AST/node",
- {node_gc_mark, RUBY_TYPED_DEFAULT_FREE, 0,},
+ {node_gc_mark, RUBY_TYPED_DEFAULT_FREE, node_memsize,},
0, 0,
RUBY_TYPED_FREE_IMMEDIATELY,
};
@@ -31,7 +44,7 @@ static const rb_data_type_t rb_node_type = {
static VALUE rb_ast_node_alloc(VALUE klass);
static void
-setup_node(VALUE obj, rb_ast_t *ast, NODE *node)
+setup_node(VALUE obj, rb_ast_t *ast, const NODE *node)
{
struct ASTNodeData *data;
@@ -41,7 +54,7 @@ setup_node(VALUE obj, rb_ast_t *ast, NODE *node)
}
static VALUE
-ast_new_internal(rb_ast_t *ast, NODE *node)
+ast_new_internal(rb_ast_t *ast, const NODE *node)
{
VALUE obj;
@@ -51,76 +64,207 @@ ast_new_internal(rb_ast_t *ast, NODE *node)
return obj;
}
-/*
- * call-seq:
- * RubyVM::AST.parse("...") -> RubyVM::AST::Node
- *
- * Parses the given string into an abstract systax tree,
- * returning the root node of that tree.
- *
- * Returns <code>nil</code> if the given string is invalid syntax.
- *
- * RubyVM::AST.parse("1 + 1") # => #<RubyVM::AST::Node(NODE_SCOPE(0) 1:0, 1:5): >
- */
+static VALUE rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens);
+static VALUE rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens);
+
static VALUE
-rb_ast_s_parse(VALUE module, VALUE str)
+ast_parse_new(void)
{
- VALUE obj;
- rb_ast_t *ast = 0;
-
- const VALUE parser = rb_parser_new();
-
- str = rb_check_string_type(str);
- rb_parser_set_context(parser, NULL, 0);
- ast = rb_parser_compile_string_path(parser, rb_str_new_cstr("no file name"), str, 1);
+ return rb_parser_set_context(rb_parser_new(), NULL, 0);
+}
+static VALUE
+ast_parse_done(rb_ast_t *ast)
+{
if (!ast->body.root) {
rb_ast_dispose(ast);
rb_exc_raise(GET_EC()->errinfo);
}
- obj = ast_new_internal(ast, (NODE *)ast->body.root);
+ return ast_new_internal(ast, (NODE *)ast->body.root);
+}
- return obj;
+static VALUE
+ast_s_parse(rb_execution_context_t *ec, VALUE module, VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
+{
+ return rb_ast_parse_str(str, keep_script_lines, error_tolerant, keep_tokens);
}
-/*
- * call-seq:
- * RubyVM::AST.parse_file(filepath) -> RubyVM::AST::Node
- *
- * Reads the file from <code>filepath</code>, then parses it like <code>.parse</code>,
- * returning the root node of the abstract syntax tree.
- *
- * Returns <code>nil</code> if <code>filepath</code>'s contents are not
- * valid Ruby syntax.
- *
- * RubyVM::AST.parse_file("my-app/app.rb") # => #<RubyVM::AST::Node(NODE_SCOPE(0) 1:0, 31:3): >
- */
static VALUE
-rb_ast_s_parse_file(VALUE module, VALUE path)
+rb_ast_parse_str(VALUE str, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
{
- VALUE obj, f;
rb_ast_t *ast = 0;
- rb_encoding *enc = rb_utf8_encoding();
- const VALUE parser = rb_parser_new();
+ StringValue(str);
+ VALUE vparser = ast_parse_new();
+ if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
+ ast = rb_parser_compile_string_path(vparser, Qnil, str, 1);
+ return ast_parse_done(ast);
+}
+
+static VALUE
+ast_s_parse_file(rb_execution_context_t *ec, VALUE module, VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
+{
+ return rb_ast_parse_file(path, keep_script_lines, error_tolerant, keep_tokens);
+}
+
+static VALUE
+rb_ast_parse_file(VALUE path, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
+{
+ VALUE f;
+ rb_ast_t *ast = 0;
+ rb_encoding *enc = rb_utf8_encoding();
FilePathValue(path);
f = rb_file_open_str(path, "r");
rb_funcall(f, rb_intern("set_encoding"), 2, rb_enc_from_encoding(enc), rb_str_new_cstr("-"));
- rb_parser_set_context(parser, NULL, 0);
- ast = rb_parser_compile_file_path(parser, path, f, 1);
-
+ VALUE vparser = ast_parse_new();
+ if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
+ ast = rb_parser_compile_file_path(vparser, Qnil, f, 1);
rb_io_close(f);
+ return ast_parse_done(ast);
+}
- if (!ast->body.root) {
- rb_ast_dispose(ast);
- rb_exc_raise(GET_EC()->errinfo);
+static VALUE
+lex_array(VALUE array, int index)
+{
+ VALUE str = rb_ary_entry(array, index);
+ if (!NIL_P(str)) {
+ StringValue(str);
+ if (!rb_enc_asciicompat(rb_enc_get(str))) {
+ rb_raise(rb_eArgError, "invalid source encoding");
+ }
}
+ return str;
+}
+
+static VALUE
+rb_ast_parse_array(VALUE array, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
+{
+ rb_ast_t *ast = 0;
- obj = ast_new_internal(ast, (NODE *)ast->body.root);
+ array = rb_check_array_type(array);
+ VALUE vparser = ast_parse_new();
+ if (RTEST(keep_script_lines)) rb_parser_keep_script_lines(vparser);
+ if (RTEST(error_tolerant)) rb_parser_error_tolerant(vparser);
+ if (RTEST(keep_tokens)) rb_parser_keep_tokens(vparser);
+ ast = rb_parser_compile_generic(vparser, lex_array, Qnil, array, 1);
+ return ast_parse_done(ast);
+}
- return obj;
+static VALUE node_children(rb_ast_t*, const NODE*);
+
+static VALUE
+node_find(VALUE self, const int node_id)
+{
+ VALUE ary;
+ long i;
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ if (nd_node_id(data->node) == node_id) return self;
+
+ ary = node_children(data->ast, data->node);
+
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ VALUE child = RARRAY_AREF(ary, i);
+
+ if (CLASS_OF(child) == rb_cNode) {
+ VALUE result = node_find(child, node_id);
+ if (RTEST(result)) return result;
+ }
+ }
+
+ return Qnil;
+}
+
+extern VALUE rb_e_script;
+
+static VALUE
+script_lines(VALUE path)
+{
+ VALUE hash, lines;
+ ID script_lines;
+ CONST_ID(script_lines, "SCRIPT_LINES__");
+ if (!rb_const_defined_at(rb_cObject, script_lines)) return Qnil;
+ hash = rb_const_get_at(rb_cObject, script_lines);
+ if (!RB_TYPE_P(hash, T_HASH)) return Qnil;
+ lines = rb_hash_lookup(hash, path);
+ if (!RB_TYPE_P(lines, T_ARRAY)) return Qnil;
+ return lines;
+}
+
+static VALUE
+node_id_for_backtrace_location(rb_execution_context_t *ec, VALUE module, VALUE location)
+{
+ int node_id;
+
+ if (!rb_frame_info_p(location)) {
+ rb_raise(rb_eTypeError, "Thread::Backtrace::Location object expected");
+ }
+
+ node_id = rb_get_node_id_from_frame_info(location);
+ if (node_id == -1) {
+ return Qnil;
+ }
+
+ return INT2NUM(node_id);
+}
+
+static VALUE
+ast_s_of(rb_execution_context_t *ec, VALUE module, VALUE body, VALUE keep_script_lines, VALUE error_tolerant, VALUE keep_tokens)
+{
+ VALUE node, lines = Qnil;
+ const rb_iseq_t *iseq;
+ int node_id;
+
+ if (rb_frame_info_p(body)) {
+ iseq = rb_get_iseq_from_frame_info(body);
+ node_id = rb_get_node_id_from_frame_info(body);
+ }
+ else {
+ iseq = NULL;
+
+ if (rb_obj_is_proc(body)) {
+ iseq = vm_proc_iseq(body);
+
+ if (!rb_obj_is_iseq((VALUE)iseq)) return Qnil;
+ }
+ else {
+ iseq = rb_method_iseq(body);
+ }
+ if (iseq) {
+ node_id = ISEQ_BODY(iseq)->location.node_id;
+ }
+ }
+
+ if (!iseq) {
+ return Qnil;
+ }
+ lines = ISEQ_BODY(iseq)->variable.script_lines;
+
+ VALUE path = rb_iseq_path(iseq);
+ int e_option = RSTRING_LEN(path) == 2 && memcmp(RSTRING_PTR(path), "-e", 2) == 0;
+
+ if (NIL_P(lines) && rb_iseq_from_eval_p(iseq) && !e_option) {
+ rb_raise(rb_eArgError, "cannot get AST for method defined in eval");
+ }
+
+ if (!NIL_P(lines) || !NIL_P(lines = script_lines(path))) {
+ node = rb_ast_parse_array(lines, keep_script_lines, error_tolerant, keep_tokens);
+ }
+ else if (e_option) {
+ node = rb_ast_parse_str(rb_e_script, keep_script_lines, error_tolerant, keep_tokens);
+ }
+ else {
+ node = rb_ast_parse_file(path, keep_script_lines, error_tolerant, keep_tokens);
+ }
+
+ return node_find(node, node_id);
}
static VALUE
@@ -133,29 +277,27 @@ rb_ast_node_alloc(VALUE klass)
}
static const char*
-node_type_to_str(NODE *node)
-{
- return ruby_node_name(nd_type(node));
-}
-
-/*
- * call-seq:
- * node.type -> string
- *
- * Returns the type of node parsed into <code>code</code>.
- *
- * root = RubyVM::AST.parse("1 + 1")
- * root.type # => "NODE_SCOPE"
- * call = root.children[1]
- * call.type # => "NODE_OPCALL"
- */
+node_type_to_str(const NODE *node)
+{
+ return (ruby_node_name(nd_type(node)) + rb_strlen_lit("NODE_"));
+}
+
static VALUE
-rb_ast_node_type(VALUE self)
+ast_node_type(rb_execution_context_t *ec, VALUE self)
{
struct ASTNodeData *data;
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
- return rb_fstring_cstr(node_type_to_str(data->node));
+ return rb_sym_intern_ascii_cstr(node_type_to_str(data->node));
+}
+
+static VALUE
+ast_node_node_id(rb_execution_context_t *ec, VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return INT2FIX(nd_node_id(data->node));
}
#define NEW_CHILD(ast, node) node ? ast_new_internal(ast, node) : Qnil
@@ -180,13 +322,13 @@ rb_ary_new_from_node_args(rb_ast_t *ast, long n, ...)
}
static VALUE
-dump_block(rb_ast_t *ast, NODE *node)
+dump_block(rb_ast_t *ast, const NODE *node)
{
VALUE ary = rb_ary_new();
do {
rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
} while (node->nd_next &&
- nd_type(node->nd_next) == NODE_BLOCK &&
+ nd_type_p(node->nd_next, NODE_BLOCK) &&
(node = node->nd_next, 1));
if (node->nd_next) {
rb_ary_push(ary, NEW_CHILD(ast, node->nd_next));
@@ -196,12 +338,12 @@ dump_block(rb_ast_t *ast, NODE *node)
}
static VALUE
-dump_array(rb_ast_t *ast, NODE *node)
+dump_array(rb_ast_t *ast, const NODE *node)
{
VALUE ary = rb_ary_new();
rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
- while (node->nd_next && nd_type(node->nd_next) == NODE_ARRAY) {
+ while (node->nd_next && nd_type_p(node->nd_next, NODE_LIST)) {
node = node->nd_next;
rb_ary_push(ary, NEW_CHILD(ast, node->nd_head));
}
@@ -219,7 +361,21 @@ var_name(ID id)
}
static VALUE
-node_children(rb_ast_t *ast, NODE *node)
+no_name_rest(void)
+{
+ ID rest;
+ CONST_ID(rest, "NODE_SPECIAL_NO_NAME_REST");
+ return ID2SYM(rest);
+}
+
+static VALUE
+rest_arg(rb_ast_t *ast, const NODE *rest_arg)
+{
+ return NODE_NAMED_REST_P(rest_arg) ? NEW_CHILD(ast, rest_arg) : no_name_rest();
+}
+
+static VALUE
+node_children(rb_ast_t *ast, const NODE *node)
{
char name[DECIMAL_SIZE_OF_BITS(sizeof(long) * CHAR_BIT) + 2]; /* including '$' */
@@ -235,24 +391,24 @@ node_children(rb_ast_t *ast, NODE *node)
return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
case NODE_CASE2:
return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
+ case NODE_CASE3:
+ return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_body);
case NODE_WHEN:
return rb_ary_new_from_node_args(ast, 3, node->nd_head, node->nd_body, node->nd_next);
+ case NODE_IN:
+ return rb_ary_new_from_node_args(ast, 3, node->nd_head, node->nd_body, node->nd_next);
case NODE_WHILE:
- goto loop;
case NODE_UNTIL:
- loop:
- return rb_ary_new_from_node_args(ast, 2, node->nd_cond, node->nd_body);
+ return rb_ary_push(rb_ary_new_from_node_args(ast, 2, node->nd_cond, node->nd_body),
+ RBOOL(node->nd_state));
case NODE_ITER:
case NODE_FOR:
return rb_ary_new_from_node_args(ast, 2, node->nd_iter, node->nd_body);
case NODE_FOR_MASGN:
return rb_ary_new_from_node_args(ast, 1, node->nd_var);
case NODE_BREAK:
- goto jump;
case NODE_NEXT:
- goto jump;
case NODE_RETURN:
- jump:
return rb_ary_new_from_node_args(ast, 1, node->nd_stts);
case NODE_REDO:
return rb_ary_new_from_node_args(ast, 0);
@@ -267,15 +423,13 @@ node_children(rb_ast_t *ast, NODE *node)
case NODE_ENSURE:
return rb_ary_new_from_node_args(ast, 2, node->nd_head, node->nd_ensr);
case NODE_AND:
- goto andor;
case NODE_OR:
- andor:
{
VALUE ary = rb_ary_new();
while (1) {
rb_ary_push(ary, NEW_CHILD(ast, node->nd_1st));
- if (!node->nd_2nd || nd_type(node->nd_2nd) != (int)type)
+ if (!node->nd_2nd || !nd_type_p(node->nd_2nd, type))
break;
node = node->nd_2nd;
}
@@ -286,23 +440,20 @@ node_children(rb_ast_t *ast, NODE *node)
if (NODE_NAMED_REST_P(node->nd_args)) {
return rb_ary_new_from_node_args(ast, 3, node->nd_value, node->nd_head, node->nd_args);
}
- return rb_ary_new_from_node_args(ast, 2, node->nd_value, node->nd_head);
+ else {
+ return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_value),
+ NEW_CHILD(ast, node->nd_head),
+ no_name_rest());
+ }
case NODE_LASGN:
- goto asgn;
case NODE_DASGN:
- goto asgn;
- case NODE_DASGN_CURR:
- goto asgn;
case NODE_IASGN:
- goto asgn;
case NODE_CVASGN:
- asgn:
+ case NODE_GASGN:
if (NODE_REQUIRED_KEYWORD_P(node)) {
- return rb_ary_new_from_args(1, var_name(node->nd_vid));
+ return rb_ary_new_from_args(2, var_name(node->nd_vid), ID2SYM(rb_intern("NODE_SPECIAL_REQUIRED_KEYWORD")));
}
return rb_ary_new_from_args(2, var_name(node->nd_vid), NEW_CHILD(ast, node->nd_value));
- case NODE_GASGN:
- goto asgn;
case NODE_CDECL:
if (node->nd_vid) {
return rb_ary_new_from_args(2, ID2SYM(node->nd_vid), NEW_CHILD(ast, node->nd_value));
@@ -314,9 +465,10 @@ node_children(rb_ast_t *ast, NODE *node)
NEW_CHILD(ast, node->nd_args->nd_head),
NEW_CHILD(ast, node->nd_args->nd_body));
case NODE_OP_ASGN2:
- return rb_ary_new_from_args(4, NEW_CHILD(ast, node->nd_recv),
- node->nd_next->nd_aid ? Qtrue : Qfalse,
+ return rb_ary_new_from_args(5, NEW_CHILD(ast, node->nd_recv),
+ RBOOL(node->nd_next->nd_aid),
ID2SYM(node->nd_next->nd_vid),
+ ID2SYM(node->nd_next->nd_mid),
NEW_CHILD(ast, node->nd_value));
case NODE_OP_ASGN_AND:
return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_head), ID2SYM(idANDOP),
@@ -343,12 +495,10 @@ node_children(rb_ast_t *ast, NODE *node)
return rb_ary_new_from_node_args(ast, 1, node->nd_args);
case NODE_ZSUPER:
return rb_ary_new_from_node_args(ast, 0);
- case NODE_ARRAY:
- goto ary;
+ case NODE_LIST:
case NODE_VALUES:
- ary:
return dump_array(ast, node);
- case NODE_ZARRAY:
+ case NODE_ZLIST:
return rb_ary_new_from_node_args(ast, 0);
case NODE_HASH:
return rb_ary_new_from_node_args(ast, 1, node->nd_head);
@@ -370,8 +520,6 @@ node_children(rb_ast_t *ast, NODE *node)
name[1] = (char)node->nd_nth;
name[2] = '\0';
return rb_ary_new_from_args(1, ID2SYM(rb_intern(name)));
- case NODE_MATCH:
- goto lit;
case NODE_MATCH2:
if (node->nd_args) {
return rb_ary_new_from_node_args(ast, 3, node->nd_recv, node->nd_value, node->nd_args);
@@ -379,24 +527,26 @@ node_children(rb_ast_t *ast, NODE *node)
return rb_ary_new_from_node_args(ast, 2, node->nd_recv, node->nd_value);
case NODE_MATCH3:
return rb_ary_new_from_node_args(ast, 2, node->nd_recv, node->nd_value);
+ case NODE_MATCH:
case NODE_LIT:
- goto lit;
case NODE_STR:
- goto lit;
case NODE_XSTR:
- lit:
return rb_ary_new_from_args(1, node->nd_lit);
case NODE_ONCE:
return rb_ary_new_from_node_args(ast, 1, node->nd_body);
case NODE_DSTR:
- goto dlit;
case NODE_DXSTR:
- goto dlit;
case NODE_DREGX:
- goto dlit;
case NODE_DSYM:
- dlit:
- return rb_ary_new_from_node_args(ast, 2, node->nd_next->nd_head, node->nd_next->nd_next);
+ {
+ NODE *n = node->nd_next;
+ VALUE head = Qnil, next = Qnil;
+ if (n) {
+ head = NEW_CHILD(ast, n->nd_head);
+ next = NEW_CHILD(ast, n->nd_next);
+ }
+ return rb_ary_new_from_args(3, node->nd_lit, head, next);
+ }
case NODE_EVSTR:
return rb_ary_new_from_node_args(ast, 1, node->nd_body);
case NODE_ARGSCAT:
@@ -428,13 +578,9 @@ node_children(rb_ast_t *ast, NODE *node)
case NODE_COLON3:
return rb_ary_new_from_args(1, ID2SYM(node->nd_mid));
case NODE_DOT2:
- goto dot;
case NODE_DOT3:
- goto dot;
case NODE_FLIP2:
- goto dot;
case NODE_FLIP3:
- dot:
return rb_ary_new_from_node_args(ast, 2, node->nd_beg, node->nd_end);
case NODE_SELF:
return rb_ary_new_from_node_args(ast, 0);
@@ -462,7 +608,8 @@ node_children(rb_ast_t *ast, NODE *node)
if (NODE_NAMED_REST_P(node->nd_1st)) {
return rb_ary_new_from_node_args(ast, 2, node->nd_1st, node->nd_2nd);
}
- return rb_ary_new_from_node_args(ast, 1, node->nd_2nd);
+ return rb_ary_new_from_args(2, no_name_rest(),
+ NEW_CHILD(ast, node->nd_2nd));
case NODE_ARGS:
{
struct rb_args_info *ainfo = node->nd_ainfo;
@@ -473,21 +620,56 @@ node_children(rb_ast_t *ast, NODE *node)
var_name(ainfo->first_post_arg),
INT2NUM(ainfo->post_args_num),
NEW_CHILD(ast, ainfo->post_init),
- var_name(ainfo->rest_arg),
- NEW_CHILD(ast, ainfo->kw_args),
- NEW_CHILD(ast, ainfo->kw_rest_arg),
+ (ainfo->rest_arg == NODE_SPECIAL_EXCESSIVE_COMMA
+ ? ID2SYM(rb_intern("NODE_SPECIAL_EXCESSIVE_COMMA"))
+ : var_name(ainfo->rest_arg)),
+ (ainfo->no_kwarg ? Qfalse : NEW_CHILD(ast, ainfo->kw_args)),
+ (ainfo->no_kwarg ? Qfalse : NEW_CHILD(ast, ainfo->kw_rest_arg)),
var_name(ainfo->block_arg));
}
case NODE_SCOPE:
{
- ID *tbl = node->nd_tbl;
- int i, size = tbl ? (int)*tbl++ : 0;
+ rb_ast_id_table_t *tbl = node->nd_tbl;
+ int i, size = tbl ? tbl->size : 0;
VALUE locals = rb_ary_new_capa(size);
for (i = 0; i < size; i++) {
- rb_ary_push(locals, var_name(tbl[i]));
+ rb_ary_push(locals, var_name(tbl->ids[i]));
}
return rb_ary_new_from_args(3, locals, NEW_CHILD(ast, node->nd_args), NEW_CHILD(ast, node->nd_body));
}
+ case NODE_ARYPTN:
+ {
+ struct rb_ary_pattern_info *apinfo = node->nd_apinfo;
+ VALUE rest = rest_arg(ast, apinfo->rest_arg);
+ return rb_ary_new_from_args(4,
+ NEW_CHILD(ast, node->nd_pconst),
+ NEW_CHILD(ast, apinfo->pre_args),
+ rest,
+ NEW_CHILD(ast, apinfo->post_args));
+ }
+ case NODE_FNDPTN:
+ {
+ struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo;
+ VALUE pre_rest = rest_arg(ast, fpinfo->pre_rest_arg);
+ VALUE post_rest = rest_arg(ast, fpinfo->post_rest_arg);
+ return rb_ary_new_from_args(4,
+ NEW_CHILD(ast, node->nd_pconst),
+ pre_rest,
+ NEW_CHILD(ast, fpinfo->args),
+ post_rest);
+ }
+ case NODE_HSHPTN:
+ {
+ VALUE kwrest = node->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD ? ID2SYM(rb_intern("NODE_SPECIAL_NO_REST_KEYWORD")) :
+ NEW_CHILD(ast, node->nd_pkwrestarg);
+
+ return rb_ary_new_from_args(3,
+ NEW_CHILD(ast, node->nd_pconst),
+ NEW_CHILD(ast, node->nd_pkwargs),
+ kwrest);
+ }
+ case NODE_ERROR:
+ return rb_ary_new_from_node_args(ast, 0);
case NODE_ARGS_AUX:
case NODE_LAST:
break;
@@ -496,17 +678,8 @@ node_children(rb_ast_t *ast, NODE *node)
rb_bug("node_children: unknown node: %s", ruby_node_name(type));
}
-/*
- * call-seq:
- * node.children -> array
- *
- * Returns AST nodes under this one. Each kind of node
- * has different children, depending on what kind of node it is.
- *
- * The returned array may contain other nodes or <code>nil</code>.
- */
static VALUE
-rb_ast_node_children(VALUE self)
+ast_node_children(rb_execution_context_t *ec, VALUE self)
{
struct ASTNodeData *data;
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
@@ -514,14 +687,8 @@ rb_ast_node_children(VALUE self)
return node_children(data->ast, data->node);
}
-/*
- * call-seq:
- * node.first_lineno -> integer
- *
- * The line number in the source code where this AST's text began.
- */
static VALUE
-rb_ast_node_first_lineno(VALUE self)
+ast_node_first_lineno(rb_execution_context_t *ec, VALUE self)
{
struct ASTNodeData *data;
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
@@ -529,14 +696,8 @@ rb_ast_node_first_lineno(VALUE self)
return INT2NUM(nd_first_lineno(data->node));
}
-/*
- * call-seq:
- * node.first_column -> integer
- *
- * The column number in the source code where this AST's text began.
- */
static VALUE
-rb_ast_node_first_column(VALUE self)
+ast_node_first_column(rb_execution_context_t *ec, VALUE self)
{
struct ASTNodeData *data;
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
@@ -544,14 +705,8 @@ rb_ast_node_first_column(VALUE self)
return INT2NUM(nd_first_column(data->node));
}
-/*
- * call-seq:
- * node.last_lineno -> integer
- *
- * The line number in the source code where this AST's text ended.
- */
static VALUE
-rb_ast_node_last_lineno(VALUE self)
+ast_node_last_lineno(rb_execution_context_t *ec, VALUE self)
{
struct ASTNodeData *data;
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
@@ -559,14 +714,8 @@ rb_ast_node_last_lineno(VALUE self)
return INT2NUM(nd_last_lineno(data->node));
}
-/*
- * call-seq:
- * node.last_column -> integer
- *
- * The column number in the source code where this AST's text ended.
- */
static VALUE
-rb_ast_node_last_column(VALUE self)
+ast_node_last_column(rb_execution_context_t *ec, VALUE self)
{
struct ASTNodeData *data;
TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
@@ -574,14 +723,17 @@ rb_ast_node_last_column(VALUE self)
return INT2NUM(nd_last_column(data->node));
}
-/*
- * call-seq:
- * node.inspect -> string
- *
- * Print this node for debugging.
- */
static VALUE
-rb_ast_node_inspect(VALUE self)
+ast_node_all_tokens(rb_execution_context_t *ec, VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ return rb_ast_tokens(data->ast);
+}
+
+static VALUE
+ast_node_inspect(rb_execution_context_t *ec, VALUE self)
{
VALUE str;
VALUE cname;
@@ -592,36 +744,30 @@ rb_ast_node_inspect(VALUE self)
str = rb_str_new2("#<");
rb_str_append(str, cname);
- rb_str_cat2(str, "(");
- rb_str_catf(str, "%s(%d) %d:%d, %d:%d", node_type_to_str(data->node), nd_type(data->node), nd_first_lineno(data->node), nd_first_column(data->node), nd_last_lineno(data->node), nd_last_column(data->node));
- rb_str_cat2(str, "): >");
+ rb_str_catf(str, ":%s@%d:%d-%d:%d>",
+ node_type_to_str(data->node),
+ nd_first_lineno(data->node), nd_first_column(data->node),
+ nd_last_lineno(data->node), nd_last_column(data->node));
return str;
}
+static VALUE
+ast_node_script_lines(rb_execution_context_t *ec, VALUE self)
+{
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+ VALUE ret = data->ast->body.script_lines;
+ if (!RB_TYPE_P(ret, T_ARRAY)) return Qnil;
+ return ret;
+}
+
+#include "ast.rbinc"
+
void
Init_ast(void)
{
- /*
- * AST has methods to parse Ruby code into
- * abstract syntax trees. The nodes in the tree
- * are instances of RubyVM::AST::Node.
- */
- rb_mAST = rb_define_module_under(rb_cRubyVM, "AST");
- /*
- * RubyVM::AST::Node instances are created by parse methods in
- * RubyVM::AST.
- */
+ rb_mAST = rb_define_module_under(rb_cRubyVM, "AbstractSyntaxTree");
rb_cNode = rb_define_class_under(rb_mAST, "Node", rb_cObject);
-
rb_undef_alloc_func(rb_cNode);
- rb_define_singleton_method(rb_mAST, "parse", rb_ast_s_parse, 1);
- rb_define_singleton_method(rb_mAST, "parse_file", rb_ast_s_parse_file, 1);
- rb_define_method(rb_cNode, "type", rb_ast_node_type, 0);
- rb_define_method(rb_cNode, "first_lineno", rb_ast_node_first_lineno, 0);
- rb_define_method(rb_cNode, "first_column", rb_ast_node_first_column, 0);
- rb_define_method(rb_cNode, "last_lineno", rb_ast_node_last_lineno, 0);
- rb_define_method(rb_cNode, "last_column", rb_ast_node_last_column, 0);
- rb_define_method(rb_cNode, "children", rb_ast_node_children, 0);
- rb_define_method(rb_cNode, "inspect", rb_ast_node_inspect, 0);
}
diff --git a/ast.rb b/ast.rb
new file mode 100644
index 0000000000..f3f72c747f
--- /dev/null
+++ b/ast.rb
@@ -0,0 +1,276 @@
+# for ast.c
+
+# AbstractSyntaxTree provides methods to parse Ruby code into
+# abstract syntax trees. The nodes in the tree
+# are instances of RubyVM::AbstractSyntaxTree::Node.
+#
+# This module is MRI specific as it exposes implementation details
+# of the MRI abstract syntax tree.
+#
+# This module is experimental and its API is not stable, therefore it might
+# change without notice. As examples, the order of children nodes is not
+# guaranteed, the number of children nodes might change, there is no way to
+# access children nodes by name, etc.
+#
+# If you are looking for a stable API or an API working under multiple Ruby
+# implementations, consider using the _parser_ gem or Ripper. If you would
+# like to make RubyVM::AbstractSyntaxTree stable, please join the discussion
+# at https://bugs.ruby-lang.org/issues/14844.
+#
+module RubyVM::AbstractSyntaxTree
+
+ # call-seq:
+ # RubyVM::AbstractSyntaxTree.parse(string, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ #
+ # Parses the given _string_ into an abstract syntax tree,
+ # returning the root node of that tree.
+ #
+ # RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
+ # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-1:9>
+ #
+ # If <tt>keep_script_lines: true</tt> option is provided, the text of the parsed
+ # source is associated with nodes and is available via Node#script_lines.
+ #
+ # If <tt>keep_tokens: true</tt> option is provided, Node#tokens are populated.
+ #
+ # SyntaxError is raised if the given _string_ is invalid syntax. To overwrite this
+ # behavior, <tt>error_tolerant: true</tt> can be provided. In this case, the parser
+ # will produce a tree where expressions with syntax errors would be represented by
+ # Node with <tt>type=:ERROR</tt>.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2")
+ # # <internal:ast>:33:in `parse': syntax error, unexpected ';', expecting ')' (SyntaxError)
+ # # x = 1; p(x; y=2
+ # # ^
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1; p(x; y=2", error_tolerant: true)
+ # # (SCOPE@1:0-1:15
+ # # tbl: [:x, :y]
+ # # args: nil
+ # # body: (BLOCK@1:0-1:15 (LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)) (ERROR@1:7-1:11) (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))))
+ # root.children.last.children
+ # # [(LASGN@1:0-1:5 :x (LIT@1:4-1:5 1)),
+ # # (ERROR@1:7-1:11),
+ # # (LASGN@1:12-1:15 :y (LIT@1:14-1:15 2))]
+ #
+ # Note that parsing continues even after the errored expresion.
+ #
+ def self.parse string, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_parse string, keep_script_lines, error_tolerant, keep_tokens
+ end
+
+ # call-seq:
+ # RubyVM::AbstractSyntaxTree.parse_file(pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ #
+ # Reads the file from _pathname_, then parses it like ::parse,
+ # returning the root node of the abstract syntax tree.
+ #
+ # SyntaxError is raised if _pathname_'s contents are not
+ # valid Ruby syntax.
+ #
+ # RubyVM::AbstractSyntaxTree.parse_file("my-app/app.rb")
+ # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-31:3>
+ #
+ # See ::parse for explanation of keyword argument meaning and usage.
+ def self.parse_file pathname, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_parse_file pathname, keep_script_lines, error_tolerant, keep_tokens
+ end
+
+ # call-seq:
+ # RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ # RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: false, error_tolerant: false, keep_tokens: false) -> RubyVM::AbstractSyntaxTree::Node
+ #
+ # Returns AST nodes of the given _proc_ or _method_.
+ #
+ # RubyVM::AbstractSyntaxTree.of(proc {1 + 2})
+ # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:35-1:42>
+ #
+ # def hello
+ # puts "hello, world"
+ # end
+ #
+ # RubyVM::AbstractSyntaxTree.of(method(:hello))
+ # # => #<RubyVM::AbstractSyntaxTree::Node:SCOPE@1:0-3:3>
+ #
+ # See ::parse for explanation of keyword argument meaning and usage.
+ def self.of body, keep_script_lines: false, error_tolerant: false, keep_tokens: false
+ Primitive.ast_s_of body, keep_script_lines, error_tolerant, keep_tokens
+ end
+
+ # call-seq:
+ # RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(backtrace_location) -> integer
+ #
+ # Returns the node id for the given backtrace location.
+ #
+ # begin
+ # raise
+ # rescue => e
+ # loc = e.backtrace_locations.first
+ # RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location(loc)
+ # end # => 0
+ def self.node_id_for_backtrace_location backtrace_location
+ Primitive.node_id_for_backtrace_location backtrace_location
+ end
+
+ # RubyVM::AbstractSyntaxTree::Node instances are created by parse methods in
+ # RubyVM::AbstractSyntaxTree.
+ #
+ # This class is MRI specific.
+ #
+ class Node
+
+ # call-seq:
+ # node.type -> symbol
+ #
+ # Returns the type of this node as a symbol.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2")
+ # root.type # => :SCOPE
+ # lasgn = root.children[2]
+ # lasgn.type # => :LASGN
+ # call = lasgn.children[1]
+ # call.type # => :OPCALL
+ def type
+ Primitive.ast_node_type
+ end
+
+ # call-seq:
+ # node.first_lineno -> integer
+ #
+ # The line number in the source code where this AST's text began.
+ def first_lineno
+ Primitive.ast_node_first_lineno
+ end
+
+ # call-seq:
+ # node.first_column -> integer
+ #
+ # The column number in the source code where this AST's text began.
+ def first_column
+ Primitive.ast_node_first_column
+ end
+
+ # call-seq:
+ # node.last_lineno -> integer
+ #
+ # The line number in the source code where this AST's text ended.
+ def last_lineno
+ Primitive.ast_node_last_lineno
+ end
+
+ # call-seq:
+ # node.last_column -> integer
+ #
+ # The column number in the source code where this AST's text ended.
+ def last_column
+ Primitive.ast_node_last_column
+ end
+
+ # call-seq:
+ # node.tokens -> array
+ #
+ # Returns tokens corresponding to the location of the node.
+ # Returns +nil+ if +keep_tokens+ is not enabled when #parse method is called.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ # root.tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ # root.tokens.map{_1[2]}.join # => "x = 1 + 2"
+ #
+ # Token is an array of:
+ #
+ # - id
+ # - token type
+ # - source code text
+ # - location [ first_lineno, first_column, last_lineno, last_column ]
+ def tokens
+ return nil unless all_tokens
+
+ all_tokens.each_with_object([]) do |token, a|
+ loc = token.last
+ if ([first_lineno, first_column] <=> [loc[0], loc[1]]) <= 0 &&
+ ([last_lineno, last_column] <=> [loc[2], loc[3]]) >= 0
+ a << token
+ end
+ end
+ end
+
+ # call-seq:
+ # node.all_tokens -> array
+ #
+ # Returns all tokens for the input script regardless the receiver node.
+ # Returns +nil+ if +keep_tokens+ is not enabled when #parse method is called.
+ #
+ # root = RubyVM::AbstractSyntaxTree.parse("x = 1 + 2", keep_tokens: true)
+ # root.all_tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ # root.children[-1].all_tokens # => [[0, :tIDENTIFIER, "x", [1, 0, 1, 1]], [1, :tSP, " ", [1, 1, 1, 2]], ...]
+ def all_tokens
+ Primitive.ast_node_all_tokens
+ end
+
+ # call-seq:
+ # node.children -> array
+ #
+ # Returns AST nodes under this one. Each kind of node
+ # has different children, depending on what kind of node it is.
+ #
+ # The returned array may contain other nodes or <code>nil</code>.
+ def children
+ Primitive.ast_node_children
+ end
+
+ # call-seq:
+ # node.inspect -> string
+ #
+ # Returns debugging information about this node as a string.
+ def inspect
+ Primitive.ast_node_inspect
+ end
+
+ # call-seq:
+ # node.node_id -> integer
+ #
+ # Returns an internal node_id number.
+ # Note that this is an API for ruby internal use, debugging,
+ # and research. Do not use this for any other purpose.
+ # The compatibility is not guaranteed.
+ def node_id
+ Primitive.ast_node_node_id
+ end
+
+ # call-seq:
+ # node.script_lines -> array
+ #
+ # Returns the original source code as an array of lines.
+ #
+ # Note that this is an API for ruby internal use, debugging,
+ # and research. Do not use this for any other purpose.
+ # The compatibility is not guaranteed.
+ def script_lines
+ Primitive.ast_node_script_lines
+ end
+
+ # call-seq:
+ # node.source -> string
+ #
+ # Returns the code fragment that corresponds to this AST.
+ #
+ # Note that this is an API for ruby internal use, debugging,
+ # and research. Do not use this for any other purpose.
+ # The compatibility is not guaranteed.
+ #
+ # Also note that this API may return an incomplete code fragment
+ # that does not parse; for example, a here document following
+ # an expression may be dropped.
+ def source
+ lines = script_lines
+ if lines
+ lines = lines[first_lineno - 1 .. last_lineno - 1]
+ lines[-1] = lines[-1][0...last_column]
+ lines[0] = lines[0][first_column..-1]
+ lines.join
+ else
+ nil
+ end
+ end
+ end
+end
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000000..f8cdf3c0c1
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+PWD=
+case "$0" in
+*/*) srcdir=`dirname $0`;;
+*) srcdir="";;
+esac
+
+symlink='--install --symlink'
+case " $* " in
+ *" -i "*|*" --install "*)
+ # reset to copy missing standard auxiliary files, instead of symlinks
+ symlink=
+ ;;
+esac
+
+exec ${AUTORECONF:-autoreconf} ${symlink} "$@" ${srcdir:+"$srcdir"}
diff --git a/basictest/test.rb b/basictest/test.rb
index 25d52ca1ef..95875b52a6 100755
--- a/basictest/test.rb
+++ b/basictest/test.rb
@@ -35,7 +35,7 @@ class Progress
# dircolors-like style
colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:\n]*)/)] : {}
begin
- File.read(File.join(__dir__, "../test/colors")).scan(/(\w+)=([^:\n]*)/) do |n, c|
+ File.read(File.join(__dir__, "../tool/colors")).scan(/(\w+)=([^:\n]*)/) do |n, c|
colors[n] ||= c
end
rescue
@@ -984,13 +984,6 @@ test_ok($z == 0)
test_check "iterator"
-test_ok(!iterator?)
-
-def ttt
- test_ok(iterator?)
-end
-ttt{}
-
# yield at top level
test_ok(!defined?(yield))
@@ -1432,9 +1425,6 @@ marity_test(:test_ok)
marity_test(:marity_test)
marity_test(:p)
-lambda(&method(:test_ok)).call(true)
-lambda(&block_get{|a,n| test_ok(a,n)}).call(true, 2)
-
class ITER_TEST1
def a
block_given?
@@ -1739,7 +1729,7 @@ a = nil
test_ok(defined?(a))
test_ok(a == nil)
-# multiple asignment
+# multiple assignment
a, b = 1, 2
test_ok(a == 1 && b == 2)
@@ -1970,6 +1960,8 @@ test_ok(p1.call == 5)
test_ok(i7 == nil)
end
+# WASI doesn't support spawning a new process for now.
+unless /wasi/ =~ RUBY_PLATFORM
test_check "system"
test_ok(`echo foobar` == "foobar\n")
test_ok(`./miniruby -e 'print "foobar"'` == 'foobar')
@@ -2020,6 +2012,7 @@ test_ok(done)
File.unlink script_tmp or `/bin/rm -f "#{script_tmp}"`
File.unlink "#{script_tmp}.bak" or `/bin/rm -f "#{script_tmp}.bak"`
+end # not /wasi/ =~ RUBY_PLATFORM
test_check "const"
TEST1 = 1
@@ -2147,7 +2140,7 @@ $_ = foobar
test_ok($_ == foobar)
class Gods
- @@rule = "Uranus" # private to Gods
+ @@rule = "Uranus"
def ruler0
@@rule
end
@@ -2170,7 +2163,7 @@ module Olympians
end
class Titans < Gods
- @@rule = "Cronus" # do not affect @@rule in Gods
+ @@rule = "Cronus" # modifies @@rule in Gods
include Olympians
def ruler4
@@rule
@@ -2185,7 +2178,14 @@ test_ok(Titans.ruler2 == "Cronus")
atlas = Titans.new
test_ok(atlas.ruler0 == "Cronus")
test_ok(atlas.ruler3 == "Zeus")
-test_ok(atlas.ruler4 == "Cronus")
+begin
+ atlas.ruler4
+rescue RuntimeError => e
+ test_ok(e.message.include?("class variable @@rule of Olympians is overtaken by Gods"))
+else
+ test_ok(false)
+end
+test_ok(atlas.ruler3 == "Zeus")
test_check "trace"
$x = 1234
diff --git a/benchmark/README.md b/benchmark/README.md
index 24a2669143..e11381cad9 100644
--- a/benchmark/README.md
+++ b/benchmark/README.md
@@ -28,15 +28,17 @@ See also:
```console
Usage: benchmark-driver [options] RUBY|YAML...
- -r, --runner TYPE Specify runner type: ips, time, memory, once (default: ips)
- -o, --output TYPE Specify output type: compare, simple, markdown, record (default: compare)
+ -r, --runner TYPE Specify runner type: ips, time, memory, once, block (default: ips)
+ -o, --output TYPE Specify output type: compare, simple, markdown, record, all (default: compare)
-e, --executables EXECS Ruby executables (e1::path1 arg1; e2::path2 arg2;...)
--rbenv VERSIONS Ruby executables in rbenv (x.x.x arg1;y.y.y arg2;...)
--repeat-count NUM Try benchmark NUM times and use the fastest result or the worst memory usage
--repeat-result TYPE Yield "best", "average" or "worst" result with --repeat-count (default: best)
+ --alternate Alternate executables instead of running the same executable in a row with --repeat-count
--bundler Install and use gems specified in Gemfile
--filter REGEXP Filter out benchmarks with given regexp
--run-duration SECONDS Warmup estimates loop_count to run for this duration (default: 3)
+ --timeout SECONDS Timeout ruby command execution with timeout(1)
-v, --verbose Verbose mode. Multiple -v options increase visilibity (max: 2)
```
@@ -53,20 +55,20 @@ make benchmark
# Or compare with specific ruby binary
make benchmark COMPARE_RUBY="/path/to/ruby --jit"
-# Run vm1 benchmarks
-make benchmark ITEM=vm1
+# Run vm benchmarks
+make benchmark ITEM=vm
# Run some limited benchmarks in ITEM-matched files
-make benchmark ITEM=vm1 OPTS=--filter=block
+make benchmark ITEM=vm OPTS=--filter=block
# You can specify the benchmark by an exact filename instead of using the default argument:
# ARGS = $$(find $(srcdir)/benchmark -maxdepth 1 -name '*$(ITEM)*.yml' -o -name '*$(ITEM)*.rb')
-make benchmark ARGS=../benchmark/erb_render.yml
+make benchmark ARGS=benchmark/erb_render.yml
# You can specify any option via $OPTS
make benchmark OPTS="--help"
# With `make benchmark`, some special runner plugins are available:
# -r peak, -r size, -r total, -r utime, -r stime, -r cutime, -r cstime
-make benchmark ITEM=vm2_bigarray OPTS="-r peak"
+make benchmark ITEM=vm_bigarray OPTS="-r peak"
```
diff --git a/benchmark/app_aobench.rb b/benchmark/app_aobench.rb
index 2bd6acfaf8..16296af12b 100644
--- a/benchmark/app_aobench.rb
+++ b/benchmark/app_aobench.rb
@@ -1,3 +1,5 @@
+# coding: US-ASCII
+
# AO render benchmark
# Original program (C) Syoyo Fujita in Javascript (and other languages)
# https://code.google.com/p/aobench/
@@ -9,6 +11,8 @@ IMAGE_HEIGHT = 256
NSUBSAMPLES = 2
NAO_SAMPLES = 8
+srand(0)
+
class Vec
def initialize(x, y, z)
@x = x
@@ -278,12 +282,14 @@ end
alias printf_orig printf
def printf *args
+ # $fp.printf(*args)
end
# File.open("ao.ppm", "w") do |fp|
+ # $fp = fp
printf("P6\n")
printf("%d %d\n", IMAGE_WIDTH, IMAGE_HEIGHT)
- printf("255\n", IMAGE_WIDTH, IMAGE_HEIGHT)
+ printf("255\n")
Scene.new.render(IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES)
# end
diff --git a/benchmark/app_erb.yml b/benchmark/app_erb.yml
index 8583858d20..31e29b7644 100644
--- a/benchmark/app_erb.yml
+++ b/benchmark/app_erb.yml
@@ -16,7 +16,6 @@ prelude: |
</html>
erb
- max = 15_000
title = "hello world!"
content = "hello world!\n" * 10
benchmark:
diff --git a/benchmark/array_flatten.yml b/benchmark/array_flatten.yml
new file mode 100644
index 0000000000..88ef544ba0
--- /dev/null
+++ b/benchmark/array_flatten.yml
@@ -0,0 +1,19 @@
+prelude: |
+ small_flat_ary = 5.times.to_a
+ large_flat_ary = 100.times.to_a
+ small_pairs_ary = [[1, 2]] * 5
+ large_pairs_ary = [[1, 2]] * 100
+ mostly_flat_ary = 100.times.to_a.push([101, 102])
+
+benchmark:
+ small_flat_ary.flatten: small_flat_ary.flatten
+ small_flat_ary.flatten!: small_flat_ary.flatten!
+ large_flat_ary.flatten: large_flat_ary.flatten
+ large_flat_ary.flatten!: large_flat_ary.flatten!
+ small_pairs_ary.flatten: small_pairs_ary.flatten
+ small_pairs_ary.flatten!: small_pairs_ary.dup.flatten!
+ large_pairs_ary.flatten: large_pairs_ary.flatten
+ large_pairs_ary.flatten!: large_pairs_ary.dup.flatten!
+ mostly_flat_ary.flatten: mostly_flat_ary.flatten
+ mostly_flat_ary.flatten!: mostly_flat_ary.dup.flatten!
+loop_count: 10000
diff --git a/benchmark/array_intersection.yml b/benchmark/array_intersection.yml
new file mode 100644
index 0000000000..26705323fd
--- /dev/null
+++ b/benchmark/array_intersection.yml
@@ -0,0 +1,14 @@
+prelude: |
+ small1 = [1, 2, 3]
+ small2 = [1, 2, 3, 4, 5]
+ small3 = [2, 3, 4, 5]
+ small4 = [2]
+ big1 = [1, 2, 3, 4] * 64
+ big2 = [1, 2, 3] * 64
+ big3 = [1, 2] * 64
+
+benchmark:
+ small-&: small1 & small2 & small3 & small4
+ small-intersection: small1.intersection(small2, small3, small4)
+ big-&: big1 & big2 & big3
+ big-intersection: big1.intersection(big2, big3)
diff --git a/benchmark/array_max_float.yml b/benchmark/array_max_float.yml
new file mode 100644
index 0000000000..ace1ae2e14
--- /dev/null
+++ b/benchmark/array_max_float.yml
@@ -0,0 +1,30 @@
+prelude: |
+ ary2 = 2.times.map(&:to_f).shuffle
+ ary10 = 10.times.map(&:to_f).shuffle
+ ary100 = 100.times.map(&:to_f).shuffle
+ ary500 = 500.times.map(&:to_f).shuffle
+ ary1000 = 1000.times.map(&:to_f).shuffle
+ ary2000 = 2500.times.map(&:to_f).shuffle
+ ary3000 = 2500.times.map(&:to_f).shuffle
+ ary5000 = 5000.times.map(&:to_f).shuffle
+ ary10000 = 10000.times.map(&:to_f).shuffle
+ ary20000 = 20000.times.map(&:to_f).shuffle
+ ary50000 = 50000.times.map(&:to_f).shuffle
+ ary100000 = 100000.times.map(&:to_f).shuffle
+
+benchmark:
+ ary2.max: ary2.max
+ ary10.max: ary10.max
+ ary100.max: ary100.max
+ ary500.max: ary500.max
+ ary1000.max: ary1000.max
+ ary2000.max: ary2000.max
+ ary3000.max: ary3000.max
+ ary5000.max: ary5000.max
+ ary10000.max: ary10000.max
+ ary20000.max: ary20000.max
+ ary50000.max: ary50000.max
+ ary100000.max: ary100000.max
+
+loop_count: 10000
+
diff --git a/benchmark/array_max_int.yml b/benchmark/array_max_int.yml
new file mode 100644
index 0000000000..acd83684d0
--- /dev/null
+++ b/benchmark/array_max_int.yml
@@ -0,0 +1,31 @@
+prelude: |
+ ary2 = 2.times.to_a.shuffle
+ ary10 = 10.times.to_a.shuffle
+ ary100 = 100.times.to_a.shuffle
+ ary500 = 500.times.to_a.shuffle
+ ary1000 = 1000.times.to_a.shuffle
+ ary2000 = 2500.times.to_a.shuffle
+ ary3000 = 2500.times.to_a.shuffle
+ ary5000 = 5000.times.to_a.shuffle
+ ary10000 = 10000.times.to_a.shuffle
+ ary20000 = 20000.times.to_a.shuffle
+ ary50000 = 50000.times.to_a.shuffle
+ ary100000 = 100000.times.to_a.shuffle
+ ary1000000 = 1000000.times.to_a.shuffle
+
+benchmark:
+ ary2.max: ary2.max
+ ary10.max: ary10.max
+ ary100.max: ary100.max
+ ary500.max: ary500.max
+ ary1000.max: ary1000.max
+ ary2000.max: ary2000.max
+ ary3000.max: ary3000.max
+ ary5000.max: ary5000.max
+ ary10000.max: ary10000.max
+ ary20000.max: ary20000.max
+ ary50000.max: ary50000.max
+ ary100000.max: ary100000.max
+ ary1000000.max: ary1000000.max
+
+loop_count: 10000
diff --git a/benchmark/array_max_str.yml b/benchmark/array_max_str.yml
new file mode 100644
index 0000000000..2aeed010f2
--- /dev/null
+++ b/benchmark/array_max_str.yml
@@ -0,0 +1,30 @@
+prelude: |
+ ary2 = 2.times.map(&:to_s).shuffle
+ ary10 = 10.times.map(&:to_s).shuffle
+ ary100 = 100.times.map(&:to_s).shuffle
+ ary500 = 500.times.map(&:to_s).shuffle
+ ary1000 = 1000.times.map(&:to_s).shuffle
+ ary2000 = 2500.times.map(&:to_s).shuffle
+ ary3000 = 2500.times.map(&:to_s).shuffle
+ ary5000 = 5000.times.map(&:to_s).shuffle
+ ary10000 = 10000.times.map(&:to_s).shuffle
+ ary20000 = 20000.times.map(&:to_s).shuffle
+ ary50000 = 50000.times.map(&:to_s).shuffle
+ ary100000 = 100000.times.map(&:to_s).shuffle
+
+benchmark:
+ ary2.max: ary2.max
+ ary10.max: ary10.max
+ ary100.max: ary100.max
+ ary500.max: ary500.max
+ ary1000.max: ary1000.max
+ ary2000.max: ary2000.max
+ ary3000.max: ary3000.max
+ ary5000.max: ary5000.max
+ ary10000.max: ary10000.max
+ ary20000.max: ary20000.max
+ ary50000.max: ary50000.max
+ ary100000.max: ary100000.max
+
+loop_count: 10000
+
diff --git a/benchmark/array_min.yml b/benchmark/array_min.yml
new file mode 100644
index 0000000000..53e5072b14
--- /dev/null
+++ b/benchmark/array_min.yml
@@ -0,0 +1,31 @@
+prelude: |
+ ary2 = 2.times.to_a.shuffle
+ ary10 = 10.times.to_a.shuffle
+ ary100 = 100.times.to_a.shuffle
+ ary500 = 500.times.to_a.shuffle
+ ary1000 = 1000.times.to_a.shuffle
+ ary2000 = 2500.times.to_a.shuffle
+ ary3000 = 2500.times.to_a.shuffle
+ ary5000 = 5000.times.to_a.shuffle
+ ary10000 = 10000.times.to_a.shuffle
+ ary20000 = 20000.times.to_a.shuffle
+ ary50000 = 50000.times.to_a.shuffle
+ ary100000 = 100000.times.to_a.shuffle
+ ary1000000 = 1000000.times.to_a.shuffle
+
+benchmark:
+ ary2.min: ary2.min
+ ary10.min: ary10.min
+ ary100.min: ary100.min
+ ary500.min: ary500.min
+ ary1000.min: ary1000.min
+ ary2000.min: ary2000.min
+ ary3000.min: ary3000.min
+ ary5000.min: ary5000.min
+ ary10000.min: ary10000.min
+ ary20000.min: ary20000.min
+ ary50000.min: ary50000.min
+ ary100000.min: ary100000.min
+ ary1000000.min: ary1000000.min
+
+loop_count: 10000
diff --git a/benchmark/array_sample.yml b/benchmark/array_sample.yml
new file mode 100644
index 0000000000..1cd2b34794
--- /dev/null
+++ b/benchmark/array_sample.yml
@@ -0,0 +1,4 @@
+prelude: ary = (1..10_000).to_a
+benchmark:
+ - ary.sample
+ - ary.sample(2)
diff --git a/benchmark/array_sort_int.yml b/benchmark/array_sort_int.yml
new file mode 100644
index 0000000000..7b9027ebf7
--- /dev/null
+++ b/benchmark/array_sort_int.yml
@@ -0,0 +1,15 @@
+prelude: |
+ ary2 = 2.times.to_a.shuffle
+ ary10 = 10.times.to_a.shuffle
+ ary100 = 100.times.to_a.shuffle
+ ary1000 = 1000.times.to_a.shuffle
+ ary10000 = 10000.times.to_a.shuffle
+
+benchmark:
+ ary2.sort: ary2.sort
+ ary10.sort: ary10.sort
+ ary100.sort: ary100.sort
+ ary1000.sort: ary1000.sort
+ ary10000.sort: ary10000.sort
+
+loop_count: 10000
diff --git a/benchmark/attr_accessor.yml b/benchmark/attr_accessor.yml
new file mode 100644
index 0000000000..82134cdf9b
--- /dev/null
+++ b/benchmark/attr_accessor.yml
@@ -0,0 +1,29 @@
+prelude: |
+ class C
+ attr_accessor :x
+ def initialize
+ @x = nil
+ end
+ class_eval <<-END
+ def ar
+ #{'x;'*256}
+ end
+ def aw
+ #{'self.x = nil;'*256}
+ end
+ def arm
+ m = method(:x)
+ #{'m.call;'*256}
+ end
+ def awm
+ m = method(:x=)
+ #{'m.call(nil);'*256}
+ end
+ END
+ end
+ obj = C.new
+benchmark:
+ attr_reader: "obj.ar"
+ attr_writer: "obj.aw"
+ attr_reader_method: "obj.arm"
+ attr_writer_method: "obj.awm"
diff --git a/benchmark/buffer_each.yml b/benchmark/buffer_each.yml
new file mode 100644
index 0000000000..417941104e
--- /dev/null
+++ b/benchmark/buffer_each.yml
@@ -0,0 +1,27 @@
+prelude: |
+ # frozen_string_literal: true
+ Warning[:experimental] = false
+ string = "The quick brown fox jumped over the lazy dog."
+ array = string.bytes
+ buffer = IO::Buffer.for(string)
+benchmark:
+ string.each_byte: |
+ upcased = String.new
+ string.each_byte do |byte|
+ upcased << (byte ^ 32)
+ end
+ array.each: |
+ upcased = String.new
+ array.each do |byte|
+ upcased << (byte ^ 32)
+ end
+ buffer.each: |
+ upcased = String.new
+ buffer.each(:U8) do |offset, byte|
+ upcased << (byte ^ 32)
+ end
+ buffer.each_byte: |
+ upcased = String.new
+ buffer.each_byte do |byte|
+ upcased << (byte ^ 32)
+ end
diff --git a/benchmark/buffer_get.yml b/benchmark/buffer_get.yml
new file mode 100644
index 0000000000..9e1f99d64e
--- /dev/null
+++ b/benchmark/buffer_get.yml
@@ -0,0 +1,25 @@
+prelude: |
+ # frozen_string_literal: true
+ Warning[:experimental] = false
+ string = "The quick brown fox jumped over the lazy dog."
+ buffer = IO::Buffer.for(string)
+ format = [:U32, :U32, :U32, :U32]
+benchmark:
+ string.unpack1: |
+ [
+ string.unpack1("N"),
+ string.unpack1("N", offset: 4),
+ string.unpack1("N", offset: 8),
+ string.unpack1("N", offset: 12),
+ ]
+ buffer.get_value: |
+ [
+ buffer.get_value(:U32, 0),
+ buffer.get_value(:U32, 4),
+ buffer.get_value(:U32, 8),
+ buffer.get_value(:U32, 12),
+ ]
+ buffer.get_values: |
+ buffer.get_values(format, 0)
+ string.unpack: |
+ string.unpack("NNNN")
diff --git a/benchmark/cgi_escape_html.yml b/benchmark/cgi_escape_html.yml
new file mode 100644
index 0000000000..655be9d7d8
--- /dev/null
+++ b/benchmark/cgi_escape_html.yml
@@ -0,0 +1,31 @@
+prelude: |
+ # frozen_string_literal: true
+ require 'cgi/escape'
+benchmark:
+ - script: CGI.escapeHTML("")
+ loop_count: 20000000
+ - script: CGI.escapeHTML("abcde")
+ loop_count: 20000000
+ - script: CGI.escapeHTML("abcd<")
+ loop_count: 20000000
+ - script: CGI.escapeHTML("'&\"<>")
+ loop_count: 5000000
+ - prelude: long_no_escape = "abcde" * 300
+ script: CGI.escapeHTML(long_no_escape)
+ loop_count: 1000000
+ - prelude: long_all_escape = "'&\"<>" * 10
+ script: CGI.escapeHTML(long_all_escape)
+ loop_count: 1000000
+ - prelude: | # http://example.com/
+ example_html = <<~HTML
+ <body>
+ <div>
+ <h1>Example Domain</h1>
+ <p>This domain is established to be used for illustrative examples in documents. You may use this
+ domain in examples without prior coordination or asking for permission.</p>
+ <p><a href="http://www.iana.org/domains/example">More information...</a></p>
+ </div>
+ </body>
+ HTML
+ script: CGI.escapeHTML(example_html)
+ loop_count: 1000000
diff --git a/benchmark/complex_float_add.yml b/benchmark/complex_float_add.yml
new file mode 100644
index 0000000000..d0150c5e5b
--- /dev/null
+++ b/benchmark/complex_float_add.yml
@@ -0,0 +1,7 @@
+prelude: |
+ max, min = 1000.0, -1000.0
+ a = Complex(rand(max)+min, rand(max)+min)
+ b = Complex(rand(max)+min, rand(max)+min)
+benchmark:
+ complex_float_add: c = a + b
+loop_count: 1000000
diff --git a/benchmark/complex_float_div.yml b/benchmark/complex_float_div.yml
new file mode 100644
index 0000000000..b9f5e1d51c
--- /dev/null
+++ b/benchmark/complex_float_div.yml
@@ -0,0 +1,7 @@
+prelude: |
+ max, min = 1000.0, -1000.0
+ a = Complex(rand(max)+min, rand(max)+min)
+ b = Complex(rand(max)+min, rand(max)+min)
+benchmark:
+ complex_float_div: c = a / b
+loop_count: 1000000
diff --git a/benchmark/complex_float_mul.yml b/benchmark/complex_float_mul.yml
new file mode 100644
index 0000000000..59b096a6dc
--- /dev/null
+++ b/benchmark/complex_float_mul.yml
@@ -0,0 +1,7 @@
+prelude: |
+ max, min = 1000.0, -1000.0
+ a = Complex(rand(max)+min, rand(max)+min)
+ b = Complex(rand(max)+min, rand(max)+min)
+benchmark:
+ complex_float_mul: c = a * b
+loop_count: 1000000
diff --git a/benchmark/complex_float_new.yml b/benchmark/complex_float_new.yml
new file mode 100644
index 0000000000..6fcde3125b
--- /dev/null
+++ b/benchmark/complex_float_new.yml
@@ -0,0 +1,7 @@
+prelude: |
+ max, min = 1000.0, -1000.0
+ a = Complex(rand(max)+min, rand(max)+min)
+ b = Complex(rand(max)+min, rand(max)+min)
+benchmark:
+ complex_float_new: c = Complex(a, b)
+loop_count: 1000000
diff --git a/benchmark/complex_float_power.yml b/benchmark/complex_float_power.yml
new file mode 100644
index 0000000000..c40a31ab55
--- /dev/null
+++ b/benchmark/complex_float_power.yml
@@ -0,0 +1,7 @@
+prelude: |
+ max, min = 1000.0, -1000.0
+ a = Complex(rand(max)+min, rand(max)+min)
+ b = Complex(rand(max)+min, rand(max)+min)
+benchmark:
+ complex_float_power: c = a ** b
+loop_count: 1000000
diff --git a/benchmark/complex_float_sub.yml b/benchmark/complex_float_sub.yml
new file mode 100644
index 0000000000..3fafe7cdbe
--- /dev/null
+++ b/benchmark/complex_float_sub.yml
@@ -0,0 +1,7 @@
+prelude: |
+ max, min = 1000.0, -1000.0
+ a = Complex(rand(max)+min, rand(max)+min)
+ b = Complex(rand(max)+min, rand(max)+min)
+benchmark:
+ complex_float_sub: c = a - b
+loop_count: 1000000
diff --git a/benchmark/constant_invalidation.rb b/benchmark/constant_invalidation.rb
new file mode 100644
index 0000000000..a95ec6f37e
--- /dev/null
+++ b/benchmark/constant_invalidation.rb
@@ -0,0 +1,22 @@
+$VERBOSE = nil
+
+CONSTANT1 = 1
+CONSTANT2 = 1
+CONSTANT3 = 1
+CONSTANT4 = 1
+CONSTANT5 = 1
+
+def constants
+ [CONSTANT1, CONSTANT2, CONSTANT3, CONSTANT4, CONSTANT5]
+end
+
+500_000.times do
+ constants
+
+ # With previous behavior, this would cause all of the constant caches
+ # associated with the constant lookups listed above to invalidate, meaning
+ # they would all have to be fetched again. With current behavior, it only
+ # invalidates when a name matches, so the following constant set shouldn't
+ # impact the constant lookups listed above.
+ INVALIDATE = true
+end
diff --git a/benchmark/enum_lazy_flat_map.yml b/benchmark/enum_lazy_flat_map.yml
new file mode 100644
index 0000000000..0ee390a441
--- /dev/null
+++ b/benchmark/enum_lazy_flat_map.yml
@@ -0,0 +1,16 @@
+prelude: |
+ num = (1..).lazy.take(100)
+ ary2 = [[1,2]].cycle.lazy.take(10)
+ ary10 = [[*1..10]].cycle.lazy.take(10)
+ ary20 = [[*1..20]].cycle.lazy.take(10)
+ ary50 = [[*1..50]].cycle.lazy.take(10)
+ ary100 = [[*1..100]].cycle.lazy.take(10)
+
+benchmark:
+ num3: num.flat_map {|x| x}.take(3).to_a
+ num10: num.flat_map {|x| x}.take(3).to_a
+ ary2: ary2.flat_map {|x| x}.take(3).to_a
+ ary10: ary10.flat_map {|x| x}.take(3).to_a
+ ary20: ary20.flat_map {|x| x}.take(3).to_a
+ ary50: ary50.flat_map {|x| x}.take(3).to_a
+ ary100: ary100.flat_map {|x| x}.take(3).to_a
diff --git a/benchmark/enum_lazy_zip.yml b/benchmark/enum_lazy_zip.yml
new file mode 100644
index 0000000000..4566ff0261
--- /dev/null
+++ b/benchmark/enum_lazy_zip.yml
@@ -0,0 +1,22 @@
+prelude: |
+ a = (1..3).lazy
+ b = a.map {|x| x}
+
+benchmark:
+ first_ary: a.zip(["a", "b", "c"]).first
+ first_nonary: a.zip("a".."c").first
+ first_noarg: a.zip.first
+
+ take3_ary: a.zip(["a", "b", "c"]).take(3).force
+ take3_nonary: a.zip("a".."c").take(3).force
+ take3_noarg: a.zip.take(3).force
+
+ chain-first_ary: b.zip(["a", "b", "c"]).first
+ chain-first_nonary: b.zip("a".."c").first
+ chain-first_noarg: b.zip.first
+
+ chain-take3_ary: b.zip(["a", "b", "c"]).take(3).force
+ chain-take3_nonary: b.zip("a".."c").take(3).force
+ chain-take3_noarg: b.zip.take(3).force
+
+ block: a.zip("a".."c") {|x, y| [x, y]}
diff --git a/benchmark/enum_minmax.yml b/benchmark/enum_minmax.yml
new file mode 100644
index 0000000000..9d01731abb
--- /dev/null
+++ b/benchmark/enum_minmax.yml
@@ -0,0 +1,25 @@
+prelude: |
+ set2 = 2.times.to_a.shuffle.to_set
+ set10 = 10.times.to_a.shuffle.to_set
+ set100 = 100.times.to_a.shuffle.to_set
+ set1000 = 1000.times.to_a.shuffle.to_set
+ set10000 = 10000.times.to_a.shuffle.to_set
+
+benchmark:
+ set2.min: set2.min
+ set10.min: set10.min
+ set100.min: set100.min
+ set1000.min: set1000.min
+ set10000.min: set10000.min
+ set2.max: set2.max
+ set10.max: set10.max
+ set100.max: set100.max
+ set1000.max: set1000.max
+ set10000.max: set10000.max
+ set2.minmax: set2.minmax
+ set10.minmax: set10.minmax
+ set100.minmax: set100.minmax
+ set1000.minmax: set1000.minmax
+ set10000.minmax: set10000.minmax
+
+loop_count: 10000
diff --git a/benchmark/enum_sort.yml b/benchmark/enum_sort.yml
new file mode 100644
index 0000000000..6f26e748c6
--- /dev/null
+++ b/benchmark/enum_sort.yml
@@ -0,0 +1,15 @@
+prelude: |
+ set2 = 2.times.to_a.shuffle.to_set
+ set10 = 10.times.to_a.shuffle.to_set
+ set100 = 100.times.to_a.shuffle.to_set
+ set1000 = 1000.times.to_a.shuffle.to_set
+ set10000 = 10000.times.to_a.shuffle.to_set
+
+benchmark:
+ set2.sort_by: set2.sort_by { 0 }
+ set10.sort_by: set10.sort_by { 0 }
+ set100.sort_by: set100.sort_by { 0 }
+ set1000.sort_by: set1000.sort_by { 0 }
+ set10000.sort_by: set10000.sort_by { 0 }
+
+loop_count: 10000
diff --git a/benchmark/enum_tally.yml b/benchmark/enum_tally.yml
new file mode 100644
index 0000000000..edd2e040a0
--- /dev/null
+++ b/benchmark/enum_tally.yml
@@ -0,0 +1,4 @@
+prelude: |
+ list = ("aaa".."zzz").to_a*10
+benchmark:
+ tally: list.tally
diff --git a/benchmark/erb_escape_html.yml b/benchmark/erb_escape_html.yml
new file mode 100644
index 0000000000..ca28d756e7
--- /dev/null
+++ b/benchmark/erb_escape_html.yml
@@ -0,0 +1,31 @@
+prelude: |
+ # frozen_string_literal: true
+ require 'erb'
+benchmark:
+ - script: ERB::Util.html_escape("")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("abcde")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("abcd<")
+ loop_count: 20000000
+ - script: ERB::Util.html_escape("'&\"<>")
+ loop_count: 5000000
+ - prelude: long_no_escape = "abcde" * 300
+ script: ERB::Util.html_escape(long_no_escape)
+ loop_count: 1000000
+ - prelude: long_all_escape = "'&\"<>" * 10
+ script: ERB::Util.html_escape(long_all_escape)
+ loop_count: 1000000
+ - prelude: | # http://example.com/
+ example_html = <<~HTML
+ <body>
+ <div>
+ <h1>Example Domain</h1>
+ <p>This domain is established to be used for illustrative examples in documents. You may use this
+ domain in examples without prior coordination or asking for permission.</p>
+ <p><a href="http://www.iana.org/domains/example">More information...</a></p>
+ </div>
+ </body>
+ HTML
+ script: ERB::Util.html_escape(example_html)
+ loop_count: 1000000
diff --git a/benchmark/fiber_chain.yml b/benchmark/fiber_chain.yml
new file mode 100644
index 0000000000..a36c759f8e
--- /dev/null
+++ b/benchmark/fiber_chain.yml
@@ -0,0 +1,36 @@
+prelude: |
+ def make_link(previous)
+ Fiber.new do
+ while message = previous.resume
+ Fiber.yield(message)
+ end
+ end
+ end
+
+ def make_chain(length = 1000, &block)
+ chain = Fiber.new(&block)
+
+ (length - 1).times do
+ chain = make_link(chain)
+ end
+
+ return chain
+ end
+
+ message = "Hello World!"
+
+ chain = make_chain do
+ while true
+ Fiber.yield(message)
+ end
+ end
+benchmark:
+ make_chain: |
+ make_chain(100) do
+ while true
+ Fiber.yield(message)
+ end
+ end
+ resume_chain: |
+ chain.resume
+loop_count: 5000
diff --git a/benchmark/fiber_locals.yml b/benchmark/fiber_locals.yml
new file mode 100644
index 0000000000..8588686477
--- /dev/null
+++ b/benchmark/fiber_locals.yml
@@ -0,0 +1,8 @@
+prelude: |
+ th = Thread.current
+ th[:key] = :val
+benchmark:
+ key?: th.key?(:key)
+ []: th[:key]
+ keys: th.keys
+loop_count: 1_000_000
diff --git a/benchmark/float_methods.yml b/benchmark/float_methods.yml
new file mode 100644
index 0000000000..56ea41effc
--- /dev/null
+++ b/benchmark/float_methods.yml
@@ -0,0 +1,14 @@
+prelude: |
+ flo = 4.2
+benchmark:
+ to_f: |
+ flo.to_f
+ abs: |
+ flo.abs
+ magnitude: |
+ flo.magnitude
+ -@: |
+ -flo
+ zero?: |
+ flo.zero?
+loop_count: 20000000
diff --git a/benchmark/float_neg_posi.yml b/benchmark/float_neg_posi.yml
new file mode 100644
index 0000000000..172db1bf6d
--- /dev/null
+++ b/benchmark/float_neg_posi.yml
@@ -0,0 +1,8 @@
+prelude: |
+ flo = 4.2
+benchmark:
+ negative?: |
+ flo.negative?
+ positive?: |
+ flo.positive?
+loop_count: 20000000
diff --git a/benchmark/float_to_s.yml b/benchmark/float_to_s.yml
new file mode 100644
index 0000000000..0abae5cdb8
--- /dev/null
+++ b/benchmark/float_to_s.yml
@@ -0,0 +1,7 @@
+prelude: |
+ floats = [*0.0.step(1.0, 0.0001)]
+
+benchmark:
+ to_s: floats.each {|f| f.to_s}
+
+loop_count: 1000
diff --git a/benchmark/gc/gcbench.rb b/benchmark/gc/gcbench.rb
index 09a404466a..23d0b91c6c 100644
--- a/benchmark/gc/gcbench.rb
+++ b/benchmark/gc/gcbench.rb
@@ -3,11 +3,12 @@ require 'pp'
require 'optparse'
$list = true
-$gcprof = true
+$gcprof = false
opt = OptionParser.new
opt.on('-q'){$list = false}
opt.on('-d'){$gcprof = false}
+opt.on('-p'){$gcprof = true}
opt.parse!(ARGV)
script = File.join(File.dirname(__FILE__), ARGV.shift)
diff --git a/benchmark/hash_aref_array.rb b/benchmark/hash_aref_array.rb
new file mode 100644
index 0000000000..ac7a683d95
--- /dev/null
+++ b/benchmark/hash_aref_array.rb
@@ -0,0 +1,5 @@
+h = {}
+arrays = (0..99).each_slice(10).to_a
+#STDERR.puts arrays.inspect
+arrays.each { |s| h[s] = s }
+200_000.times { arrays.each { |s| h[s] } }
diff --git a/benchmark/hash_defaults.yml b/benchmark/hash_defaults.yml
new file mode 100644
index 0000000000..833f10e1c7
--- /dev/null
+++ b/benchmark/hash_defaults.yml
@@ -0,0 +1,6 @@
+prelude: |
+ h = Hash.new { :foo }
+benchmark:
+ default_aref: h[1]
+ default_method: h.default(1)
+loop_count: 1000000
diff --git a/benchmark/hash_dup.yml b/benchmark/hash_dup.yml
new file mode 100644
index 0000000000..65f521ec94
--- /dev/null
+++ b/benchmark/hash_dup.yml
@@ -0,0 +1,8 @@
+prelude: |
+ small_hash = { a: 1 }
+ larger_hash = 20.times.map { |i| [('a'.ord + i).chr.to_sym, i] }.to_h
+
+benchmark:
+ dup_small: small_hash.dup
+ dup_larger: larger_hash.dup
+loop_count: 10000
diff --git a/benchmark/hash_first.yml b/benchmark/hash_first.yml
new file mode 100644
index 0000000000..c26df1a7ed
--- /dev/null
+++ b/benchmark/hash_first.yml
@@ -0,0 +1,11 @@
+prelude: |
+ hash1 = 1_000_000.times.to_h { [rand, true]}
+ hash2 = hash1.dup
+ hash2.keys[1..100_000].each { hash2.delete _1 }
+ hash2.delete hash2.first[0]
+
+benchmark:
+ hash1: hash1.first
+ hash2: hash2.first
+
+loop_count: 100_000
diff --git a/benchmark/hash_literal_small2.rb b/benchmark/hash_literal_small2.rb
new file mode 100644
index 0000000000..c188529260
--- /dev/null
+++ b/benchmark/hash_literal_small2.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+1_000_000.times.map { { "foo" => "bar", "bar" => "baz" } }
diff --git a/benchmark/hash_literal_small4.rb b/benchmark/hash_literal_small4.rb
new file mode 100644
index 0000000000..739f71b5b0
--- /dev/null
+++ b/benchmark/hash_literal_small4.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+1_000_000.times.map { { "foo" => "bar", "bar" => "baz", "baz" => "lol", "lol" => "lgtm" } }
diff --git a/benchmark/hash_literal_small8.rb b/benchmark/hash_literal_small8.rb
new file mode 100644
index 0000000000..53d80af535
--- /dev/null
+++ b/benchmark/hash_literal_small8.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+1_000_000.times.map { { "foo" => "bar", "bar" => "baz", "baz" => "lol", "lol" => "lgtm", "lgtm" => "nope", "nope" => "ok", "ok" => "again", "again" => "wait" } }
diff --git a/benchmark/io_write.rb b/benchmark/io_write.rb
new file mode 100644
index 0000000000..cdb409948b
--- /dev/null
+++ b/benchmark/io_write.rb
@@ -0,0 +1,22 @@
+#!/usr/bin/env ruby
+
+require 'benchmark'
+
+i, o = IO.pipe
+o.sync = true
+
+DOT = ".".freeze
+
+chunks = 100_000.times.collect{DOT}
+
+thread = Thread.new do
+ while i.read(1024)
+ end
+end
+
+100.times do
+ o.write(*chunks)
+end
+
+o.close
+thread.join
diff --git a/benchmark/irb_color.yml b/benchmark/irb_color.yml
new file mode 100644
index 0000000000..ebdc8d7e8b
--- /dev/null
+++ b/benchmark/irb_color.yml
@@ -0,0 +1,13 @@
+prelude: |
+ require 'irb/color'
+ code = <<~'CODE'
+ def self.foo # bar
+ :"erb #{ERB.new("<%= self %>", trim_mode: ?-).result}"
+ end
+ CODE
+benchmark:
+ irb_color_complete: |
+ IRB::Color.colorize_code(code, complete: true)
+ irb_color_incomplete: |
+ IRB::Color.colorize_code(code, complete: false)
+loop_count: 2000000
diff --git a/benchmark/irb_exec.yml b/benchmark/irb_exec.yml
new file mode 100644
index 0000000000..28933f8b38
--- /dev/null
+++ b/benchmark/irb_exec.yml
@@ -0,0 +1,10 @@
+prelude: |
+ # frozen_string_literal: true
+ require 'rbconfig'
+ irb_f = [File.join(File.dirname(RbConfig.ruby), 'irb'), '-f']
+benchmark:
+ irb_exec: |
+ IO.popen(irb_f, 'w') do |io|
+ io.write('exit')
+ end
+loop_count: 30
diff --git a/benchmark/iseq_load_from_binary.yml b/benchmark/iseq_load_from_binary.yml
new file mode 100644
index 0000000000..7e9d73bdd4
--- /dev/null
+++ b/benchmark/iseq_load_from_binary.yml
@@ -0,0 +1,25 @@
+prelude: |
+ symbol = RubyVM::InstructionSequence.compile(":foo; :bar; :baz; :egg; :spam").to_binary
+
+ define_method = RubyVM::InstructionSequence.compile(%{
+ def foo; end
+ def bar; end
+ def baz; end
+ def egg; end
+ def spam; end
+ }).to_binary
+
+ all = RubyVM::InstructionSequence.compile(%{
+ module Foo; def foo; :foo; end; end
+ module Bar; def bar; :bar; end; end
+ module Baz; def baz; :baz; end; end
+ class Egg; def egg; :egg; end; end
+ class Spaml; def spam; :spam; end; end
+ }).to_binary
+
+benchmark:
+ symbol: RubyVM::InstructionSequence.load_from_binary(symbol)
+ define_method: RubyVM::InstructionSequence.load_from_binary(define_method)
+ all: RubyVM::InstructionSequence.load_from_binary(all)
+
+loop_count: 100_000
diff --git a/benchmark/ivar_extend.yml b/benchmark/ivar_extend.yml
new file mode 100644
index 0000000000..eb9ee923f5
--- /dev/null
+++ b/benchmark/ivar_extend.yml
@@ -0,0 +1,23 @@
+prelude: |
+ class Embedded
+ def initialize
+ @a = 1
+ @b = 1
+ @c = 1
+ end
+ end
+
+ class Extended
+ def initialize
+ @a = 1
+ @b = 1
+ @c = 1
+ @d = 1
+ @e = 1
+ @f = 1
+ end
+ end
+benchmark:
+ embedded: Embedded.new
+ extended: Extended.new
+loop_count: 20_000_000
diff --git a/benchmark/kernel_clone.yml b/benchmark/kernel_clone.yml
new file mode 100644
index 0000000000..069b23abcd
--- /dev/null
+++ b/benchmark/kernel_clone.yml
@@ -0,0 +1,6 @@
+prelude: "object = Object.new"
+benchmark:
+ clone: "object.clone"
+ clone_true: "object.clone(freeze: true)"
+ clone_false: "object.clone(freeze: false)"
+loop_count: 10000
diff --git a/benchmark/kernel_float.yml b/benchmark/kernel_float.yml
new file mode 100644
index 0000000000..215f6750fc
--- /dev/null
+++ b/benchmark/kernel_float.yml
@@ -0,0 +1,5 @@
+benchmark:
+ float: "Float(42)"
+ float_true: "Float(42, exception: true)"
+ float_false: "Float(42, exception: false)"
+loop_count: 10000
diff --git a/benchmark/kernel_tap.yml b/benchmark/kernel_tap.yml
new file mode 100644
index 0000000000..4dcbb31b4d
--- /dev/null
+++ b/benchmark/kernel_tap.yml
@@ -0,0 +1,6 @@
+prelude: |
+ obj = Object.new
+ x = nil
+benchmark:
+ kernel_tap: obj.tap { |o| x = o }
+loop_count: 20000000
diff --git a/benchmark/kernel_then.yml b/benchmark/kernel_then.yml
new file mode 100644
index 0000000000..85f7341e33
--- /dev/null
+++ b/benchmark/kernel_then.yml
@@ -0,0 +1,6 @@
+benchmark:
+ kernel_then: 1.then { |i| i + 1 }
+ kernel_then_enum: 1.then
+ kernel_yield_self: 1.yield_self { |i| i + 1 }
+ kernel_yield_self_enum: 1.yield_self
+loop_count: 20000000
diff --git a/benchmark/keyword_arguments.yml b/benchmark/keyword_arguments.yml
new file mode 100644
index 0000000000..fce6bce0b8
--- /dev/null
+++ b/benchmark/keyword_arguments.yml
@@ -0,0 +1,13 @@
+prelude: |
+ h = {a: 1}
+ def kw(a: 1) a end
+ def kws(**kw) kw end
+benchmark:
+ kw_to_kw: "kw(a: 1)"
+ kw_splat_to_kw: "kw(**h)"
+ kw_to_kw_splat: "kws(a: 1)"
+ kw_splat_to_kw_splat: "kws(**h)"
+ kw_and_splat_to_kw: "kw(a: 1, **h)"
+ kw_splats_to_kw: "kw(**h, **h)"
+ kw_and_splat_to_kw_splat: "kws(a: 1, **h)"
+ kw_splats_to_kw_splat: "kws(**h, **h)"
diff --git a/benchmark/lib/benchmark_driver/runner/mjit.rb b/benchmark/lib/benchmark_driver/runner/mjit.rb
new file mode 100644
index 0000000000..3a58a620de
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/mjit.rb
@@ -0,0 +1,34 @@
+require 'benchmark_driver/struct'
+require 'benchmark_driver/metric'
+require 'erb'
+
+# A runner to measure after-JIT performance easily
+class BenchmarkDriver::Runner::Mjit < BenchmarkDriver::Runner::Ips
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob)
+
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]).extend(Module.new{
+ def parse(**)
+ jobs = super
+ jobs.map do |job|
+ job = job.dup
+ job.prelude = "#{job.prelude}\n#{<<~EOS}"
+ if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
+ __bmdv_ruby_i = 0
+ while __bmdv_ruby_i < 10000 # MJIT call threshold
+ #{job.script}
+ __bmdv_ruby_i += 1
+ end
+ RubyVM::MJIT.pause # compile
+ #{job.script}
+ RubyVM::MJIT.resume; RubyVM::MJIT.pause # recompile
+ #{job.script}
+ RubyVM::MJIT.resume; RubyVM::MJIT.pause # recompile 2
+ end
+ EOS
+ job
+ end
+ end
+ })
+end
diff --git a/benchmark/lib/benchmark_driver/runner/peak.rb b/benchmark/lib/benchmark_driver/runner/peak.rb
index 0ba8af42c5..d04f2e51ff 100644
--- a/benchmark/lib/benchmark_driver/runner/peak.rb
+++ b/benchmark/lib/benchmark_driver/runner/peak.rb
@@ -61,7 +61,7 @@ class BenchmarkDriver::Runner::Peak
loop_count: job.loop_count,
)
- memory_status = File.expand_path('../../../../test/lib/memory_status', __dir__)
+ memory_status = File.expand_path('../../../../tool/lib/memory_status', __dir__)
Tempfile.open(['benchmark_driver-', '.rb']) do |f|
with_script(benchmark.render) do |path|
output = IO.popen([*context.executable.command, path, f.path, target, memory_status], &:read)
diff --git a/benchmark/lib/benchmark_driver/runner/ractor.rb b/benchmark/lib/benchmark_driver/runner/ractor.rb
new file mode 100644
index 0000000000..c730b8e4a5
--- /dev/null
+++ b/benchmark/lib/benchmark_driver/runner/ractor.rb
@@ -0,0 +1,122 @@
+require 'erb'
+
+# A runner to measure performance *inside* Ractor
+class BenchmarkDriver::Runner::Ractor < BenchmarkDriver::Runner::Ips
+ # JobParser returns this, `BenchmarkDriver::Runner.runner_for` searches "*::Job"
+ Job = Class.new(BenchmarkDriver::DefaultJob) do
+ attr_accessor :ractor
+ end
+
+ # Dynamically fetched and used by `BenchmarkDriver::JobParser.parse`
+ JobParser = BenchmarkDriver::DefaultJobParser.for(klass: Job, metrics: [METRIC]).extend(Module.new{
+ def parse(ractor: 1, **kwargs)
+ super(**kwargs).each do |job|
+ job.ractor = ractor
+ end
+ end
+ })
+
+ private
+
+ unless private_instance_methods.include?(:run_benchmark)
+ raise "#run_benchmark is no longer defined in BenchmarkDriver::Runner::Ips"
+ end
+
+ # @param [BenchmarkDriver::Runner::Ips::Job] job - loop_count is not nil
+ # @param [BenchmarkDriver::Context] context
+ # @return [BenchmarkDriver::Metrics]
+ def run_benchmark(job, context:)
+ benchmark = BenchmarkScript.new(
+ preludes: [context.prelude, job.prelude],
+ script: job.script,
+ teardown: job.teardown,
+ loop_count: job.loop_count,
+ )
+
+ results = job.ractor.times.map do
+ Tempfile.open('benchmark_driver_result')
+ end
+ duration = with_script(benchmark.render(results: results.map(&:path))) do |path|
+ success = execute(*context.executable.command, path, exception: false)
+ if success && ((value = results.map { |f| Float(f.read) }.max) > 0)
+ value
+ else
+ BenchmarkDriver::Result::ERROR
+ end
+ end
+ results.each(&:close)
+
+ value_duration(
+ loop_count: job.loop_count,
+ duration: duration,
+ )
+ end
+
+ # @param [String] prelude
+ # @param [String] script
+ # @param [String] teardown
+ # @param [Integer] loop_count
+ BenchmarkScript = ::BenchmarkDriver::Struct.new(:preludes, :script, :teardown, :loop_count) do
+ # @param [String] result - A file to write result
+ def render(results:)
+ prelude = preludes.reject(&:nil?).reject(&:empty?).join("\n")
+ ERB.new(<<-RUBY).result_with_hash(results: results)
+Warning[:experimental] = false
+# shareable-constant-value: experimental_everything
+#{prelude}
+
+if #{loop_count} == 1
+ __bmdv_loop_before = 0
+ __bmdv_loop_after = 0
+else
+ __bmdv_loop_before = Time.new
+ #{while_loop('', loop_count, id: 0)}
+ __bmdv_loop_after = Time.new
+end
+
+__bmdv_ractors = []
+<% results.size.times do %>
+__bmdv_ractors << Ractor.new(__bmdv_loop_after - __bmdv_loop_before) { |__bmdv_loop_time|
+ __bmdv_time = Time
+ __bmdv_script_before = __bmdv_time.new
+ #{while_loop(script, loop_count, id: 1)}
+ __bmdv_script_after = __bmdv_time.new
+
+ (__bmdv_script_after - __bmdv_script_before) - __bmdv_loop_time
+}
+<% end %>
+
+# Wait for all Ractors before executing code to write results
+__bmdv_ractors.map!(&:take)
+
+<% results.each do |result| %>
+File.write(<%= result.dump %>, __bmdv_ractors.shift)
+<% end %>
+
+#{teardown}
+ RUBY
+ end
+
+ private
+
+ # id is to prevent:
+ # can not isolate a Proc because it accesses outer variables (__bmdv_i)
+ def while_loop(content, times, id:)
+ if !times.is_a?(Integer) || times <= 0
+ raise ArgumentError.new("Unexpected times: #{times.inspect}")
+ elsif times == 1
+ return content
+ end
+
+ # TODO: execute in batch
+ <<-RUBY
+__bmdv_i#{id} = 0
+while __bmdv_i#{id} < #{times}
+ #{content}
+ __bmdv_i#{id} += 1
+end
+ RUBY
+ end
+ end
+ private_constant :BenchmarkScript
+end
diff --git a/benchmark/lib/load.rb b/benchmark/lib/load.rb
new file mode 100644
index 0000000000..31b770c484
--- /dev/null
+++ b/benchmark/lib/load.rb
@@ -0,0 +1,18 @@
+# How to use this file:
+# 1. write a `$(srcdir)/test.rb` like:
+=begin
+require_relative 'benchmark/lib/load'
+
+Benchmark.driver(repeat_count: 5){|x|
+ x.executable name: 'clean-miniruby', command: %w'../clean-trunk/miniruby'
+ x.executable name: 'modif-miniruby', command: %w'./miniruby'
+
+ x.report %q{
+ h = {a: 1, b: 2, c: 3, d: 4}
+ }
+}
+=end
+#
+# 2. `make run`
+$:.unshift(File.join(__dir__, '../benchmark-driver/lib'))
+require 'benchmark_driver'
diff --git a/benchmark/marshal_dump_load_integer.yml b/benchmark/marshal_dump_load_integer.yml
new file mode 100644
index 0000000000..78ebf823d2
--- /dev/null
+++ b/benchmark/marshal_dump_load_integer.yml
@@ -0,0 +1,22 @@
+prelude: |
+ smallint_array = 1000.times.map { |x| x }
+ bigint32_array = 1000.times.map { |x| x + 2**32 }
+ bigint64_array = 1000.times.map { |x| x + 2**64 }
+
+ smallint_dump = Marshal.dump(smallint_array)
+ bigint32_dump = Marshal.dump(bigint32_array)
+ bigint64_dump = Marshal.dump(bigint64_array)
+benchmark:
+ marshal_dump_integer_small: |
+ Marshal.dump(smallint_array)
+ marshal_dump_integer_over_32_bit: |
+ Marshal.dump(bigint32_array)
+ marshal_dump_integer_over_64_bit: |
+ Marshal.dump(bigint64_array)
+ marshal_load_integer_small: |
+ Marshal.load(smallint_dump)
+ marshal_load_integer_over_32_bit: |
+ Marshal.load(bigint32_dump)
+ marshal_load_integer_over_64_bit: |
+ Marshal.load(bigint64_dump)
+loop_count: 4000
diff --git a/benchmark/masgn.yml b/benchmark/masgn.yml
new file mode 100644
index 0000000000..31cb8ee4a3
--- /dev/null
+++ b/benchmark/masgn.yml
@@ -0,0 +1,53 @@
+prelude: |
+ a = [nil] * 3
+ b = Class.new{attr_writer :a, :b, :c}.new
+ c = d = e = f = g = h = i = nil
+benchmark:
+ array2_2: "c = (a[0], a[1] = 1, 2)"
+ array2_3: "c = (a[0], a[1] = 1, 2, 3)"
+ array3_2: "c = (a[0], a[1], a[2] = 1, 2)"
+ array3_3: "c = (a[0], a[1], a[2] = 1, 2, 3)"
+ attr2_2: "c = (b.a, b.b = 1, 2)"
+ attr2_3: "c = (b.a, b.b = 1, 2, 3)"
+ attr3_2: "c = (b.a, b.b, b.c = 1, 2)"
+ attr3_3: "c = (b.a, b.b, b.c = 1, 2, 3)"
+ lvar2_2: "c = (d, e = 1, 2)"
+ lvar2_3: "c = (d, e = 1, 2, 3)"
+ lvar3_2: "c = (d, e, f = 1, 2)"
+ lvar3_3: "c = (d, e, f = 1, 2, 3)"
+ array2_2p: "(a[0], a[1] = 1, 2; nil)"
+ array2_3p: "(a[0], a[1] = 1, 2, 3; nil)"
+ array3_2p: "(a[0], a[1], a[2] = 1, 2; nil)"
+ array3_3p: "(a[0], a[1], a[2] = 1, 2, 3; nil)"
+ attr2_2p: "(b.a, b.b = 1, 2; nil)"
+ attr2_3p: "(b.a, b.b = 1, 2, 3; nil)"
+ attr3_2p: "(b.a, b.b, b.c = 1, 2; nil)"
+ attr3_3p: "(b.a, b.b, b.c = 1, 2, 3; nil)"
+ lvar2_2p: "(d, e = 1, 2; nil)"
+ lvar2_3p: "(d, e = 1, 2, 3; nil)"
+ lvar3_2p: "(d, e, f = 1, 2; nil)"
+ lvar3_3p: "(d, e, f = 1, 2, 3; nil)"
+ array2_2lv: "c = (a[0], a[1] = g, h)"
+ array2_ilv: "c = (a[0], a[1] = g, h, i)"
+ arrayi_2lv: "c = (a[0], a[1], a[2] = g, h)"
+ arrayi_ilv: "c = (a[0], a[1], a[2] = g, h, i)"
+ attr2_2lv: "c = (b.a, b.b = g, h)"
+ attr2_ilv: "c = (b.a, b.b = g, h, i)"
+ attri_2lv: "c = (b.a, b.b, b.c = g, h)"
+ attri_ilv: "c = (b.a, b.b, b.c = g, h, i)"
+ lvar2_2lv: "c = (d, e = g, h)"
+ lvar2_ilv: "c = (d, e = g, h, i)"
+ lvari_2lv: "c = (d, e, f = g, h)"
+ lvari_ilv: "c = (d, e, f = g, h, i)"
+ array2_2plv: "(a[0], a[1] = g, h; nil)"
+ array2_iplv: "(a[0], a[1] = g, h, i; nil)"
+ arrayi_2plv: "(a[0], a[1], a[2] = g, h; nil)"
+ arrayi_iplv: "(a[0], a[1], a[2] = g, h, i; nil)"
+ attr2_2plv: "(b.a, b.b = g, h; nil)"
+ attr2_iplv: "(b.a, b.b = g, h, i; nil)"
+ attri_2plv: "(b.a, b.b, b.c = g, h; nil)"
+ attri_iplv: "(b.a, b.b, b.c = g, h, i; nil)"
+ lvar2_2plv: "(d, e = g, h; nil)"
+ lvar2_iplv: "(d, e = g, h, i; nil)"
+ lvari_2plv: "(d, e, f = g, h; nil)"
+ lvari_iplv: "(d, e, f = g, h, i; nil)"
diff --git a/benchmark/match_gt4.rb b/benchmark/match_gt4.rb
new file mode 100644
index 0000000000..ffda109912
--- /dev/null
+++ b/benchmark/match_gt4.rb
@@ -0,0 +1 @@
+1000000.times { /(.)(.)(\d+)(\d)/.match("THX1138.") }
diff --git a/benchmark/match_small.rb b/benchmark/match_small.rb
new file mode 100644
index 0000000000..3b743d484a
--- /dev/null
+++ b/benchmark/match_small.rb
@@ -0,0 +1 @@
+1000000.times { 'haystack'.match(/hay/) }
diff --git a/benchmark/method_bind_call.yml b/benchmark/method_bind_call.yml
new file mode 100644
index 0000000000..9e0e046ed4
--- /dev/null
+++ b/benchmark/method_bind_call.yml
@@ -0,0 +1,16 @@
+prelude: |
+ named_module = Kernel
+
+ module FakeName
+ def self.name
+ "NotMyame".freeze
+ end
+ end
+
+ MOD_NAME = Module.instance_method(:name)
+
+benchmark:
+ fastpath: MOD_NAME.bind_call(Kernel)
+ slowpath: MOD_NAME.bind_call(FakeName)
+
+loop_count: 100_000
diff --git a/benchmark/mjit_exivar.yml b/benchmark/mjit_exivar.yml
new file mode 100644
index 0000000000..2584fa6410
--- /dev/null
+++ b/benchmark/mjit_exivar.yml
@@ -0,0 +1,18 @@
+type: lib/benchmark_driver/runner/mjit
+prelude: |
+ class Bench < Hash
+ def initialize
+ @exivar = nil
+ end
+
+ def exivar
+ @exivar
+ end
+ end
+
+ bench = Bench.new
+
+benchmark:
+ mjit_exivar: bench.exivar
+
+loop_count: 200000000
diff --git a/benchmark/mjit_integer.yml b/benchmark/mjit_integer.yml
new file mode 100644
index 0000000000..a6b5c9ee16
--- /dev/null
+++ b/benchmark/mjit_integer.yml
@@ -0,0 +1,32 @@
+type: lib/benchmark_driver/runner/mjit
+prelude: |
+ def mjit_abs(int) int.abs end
+ def mjit_bit_length(int) int.bit_length end
+ def mjit_comp(int) ~int end
+ def mjit_even?(int) int.even? end
+ def mjit_integer?(int) int.integer? end
+ def mjit_magnitude(int) int.magnitude end
+ def mjit_odd?(int) int.odd? end
+ def mjit_ord(int) int.ord end
+ def mjit_size(int) int.size end
+ def mjit_to_i(int) int.to_i end
+ def mjit_to_int(int) int.to_int end
+ def mjit_uminus(int) -int end
+ def mjit_zero?(int) int.zero? end
+
+benchmark:
+ - mjit_abs(-1)
+ - mjit_bit_length(100)
+ - mjit_comp(1)
+ - mjit_even?(2)
+ - mjit_integer?(0)
+ - mjit_magnitude(-1)
+ - mjit_odd?(1)
+ - mjit_ord(1)
+ - mjit_size(1)
+ - mjit_to_i(1)
+ - mjit_to_int(1)
+ - mjit_uminus(1)
+ - mjit_zero?(0)
+
+loop_count: 40000000
diff --git a/benchmark/mjit_kernel.yml b/benchmark/mjit_kernel.yml
new file mode 100644
index 0000000000..7720e65c2c
--- /dev/null
+++ b/benchmark/mjit_kernel.yml
@@ -0,0 +1,20 @@
+type: lib/benchmark_driver/runner/mjit
+prelude: |
+ def mjit_class(obj)
+ obj.class
+ end
+
+ def mjit_frozen?(obj)
+ obj.frozen?
+ end
+
+ str = ""
+ fstr = "".freeze
+
+benchmark:
+ - mjit_class(self)
+ - mjit_class(1)
+ - mjit_frozen?(str)
+ - mjit_frozen?(fstr)
+
+loop_count: 40000000
diff --git a/benchmark/mjit_leave.yml b/benchmark/mjit_leave.yml
new file mode 100644
index 0000000000..9ac68b164b
--- /dev/null
+++ b/benchmark/mjit_leave.yml
@@ -0,0 +1,8 @@
+type: lib/benchmark_driver/runner/mjit
+prelude: |
+ def leave
+ nil
+ end
+benchmark:
+ mjit_leave: leave
+loop_count: 200000000
diff --git a/benchmark/mjit_opt_cc_insns.yml b/benchmark/mjit_opt_cc_insns.yml
new file mode 100644
index 0000000000..fed6d34bd5
--- /dev/null
+++ b/benchmark/mjit_opt_cc_insns.yml
@@ -0,0 +1,27 @@
+# opt_* insns using vm_method_cfunc_is with send-compatible operands:
+# * opt_nil_p
+# * opt_not
+# * opt_eq
+type: lib/benchmark_driver/runner/mjit
+prelude: |
+ def mjit_nil?(obj)
+ obj.nil?
+ end
+
+ def mjit_not(obj)
+ !obj
+ end
+
+ def mjit_eq(a, b)
+ a == b
+ end
+
+benchmark:
+ - script: mjit_nil?(1)
+ loop_count: 40000000
+ - script: mjit_not(1)
+ loop_count: 40000000
+ - script: mjit_eq(1, nil)
+ loop_count: 8000000
+ - script: mjit_eq(nil, 1)
+ loop_count: 8000000
diff --git a/benchmark/mjit_struct_aref.yml b/benchmark/mjit_struct_aref.yml
new file mode 100644
index 0000000000..bfba1323f2
--- /dev/null
+++ b/benchmark/mjit_struct_aref.yml
@@ -0,0 +1,10 @@
+type: lib/benchmark_driver/runner/mjit
+prelude: |
+ def mjit_struct_aref(struct)
+ struct.aa
+ end
+ struct = Struct.new(:a0, :a1, :a2, :a3, :a4, :a5, :a6, :a7, :a8, :a9, :aa).new
+
+benchmark: mjit_struct_aref(struct)
+
+loop_count: 40000000
diff --git a/benchmark/module_eqq.yml b/benchmark/module_eqq.yml
new file mode 100644
index 0000000000..a561fb86dc
--- /dev/null
+++ b/benchmark/module_eqq.yml
@@ -0,0 +1,27 @@
+prelude: |
+ class SimpleClass; end
+ class MediumClass
+ 10.times { include Module.new }
+ end
+ class LargeClass
+ 100.times { include Module.new }
+ end
+ class HugeClass
+ 300.times { include Module.new }
+ end
+ SimpleObj = SimpleClass.new
+ MediumObj = MediumClass.new
+ LargeObj = LargeClass.new
+ HugeObj = HugeClass.new
+benchmark:
+ simple_class_eqq_simple_obj: |
+ SimpleClass === SimpleObj
+ medium_class_eqq_simple_obj: |
+ MediumClass === SimpleObj
+ simple_class_eqq_medium_obj: |
+ SimpleClass === MediumObj
+ simple_class_eqq_large_obj: |
+ SimpleClass === LargeObj
+ simple_class_eqq_huge_obj: |
+ SimpleClass === HugeObj
+loop_count: 20000000
diff --git a/benchmark/nil_p.yml b/benchmark/nil_p.yml
new file mode 100644
index 0000000000..79ba4f2177
--- /dev/null
+++ b/benchmark/nil_p.yml
@@ -0,0 +1,9 @@
+prelude: |
+ class Niller; def nil?; true; end; end
+ xnil, notnil = nil, Object.new
+ niller = Niller.new
+benchmark:
+ - xnil.nil?
+ - notnil.nil?
+ - niller.nil?
+loop_count: 10000000
diff --git a/benchmark/nilclass.yml b/benchmark/nilclass.yml
new file mode 100644
index 0000000000..fba67a5f6a
--- /dev/null
+++ b/benchmark/nilclass.yml
@@ -0,0 +1,6 @@
+benchmark:
+ to_i: |
+ nil.to_i
+ to_f: |
+ nil.to_f
+loop_count: 100000
diff --git a/benchmark/num_zero_p.yml b/benchmark/num_zero_p.yml
new file mode 100644
index 0000000000..2195963433
--- /dev/null
+++ b/benchmark/num_zero_p.yml
@@ -0,0 +1,8 @@
+benchmark:
+ - 0.zero?
+ - 1.zero?
+ - 0r.zero?
+ - 1r.zero?
+ - 0i.zero?
+ - 1i.zero?
+loop_count: 50000000
diff --git a/benchmark/numeric_methods.yml b/benchmark/numeric_methods.yml
new file mode 100644
index 0000000000..1384902935
--- /dev/null
+++ b/benchmark/numeric_methods.yml
@@ -0,0 +1,29 @@
+prelude: |
+ int = 42
+ flo = 4.2
+benchmark:
+ real?: |
+ int.real?
+ integer?: |
+ flo.integer?
+ finite?: |
+ int.finite?
+ infinite?: |
+ int.infinite?
+ integer_real: |
+ int.real
+ float_real: |
+ flo.real
+ integr_imag: |
+ int.imag
+ float_imag: |
+ flo.imag
+ integer_conj: |
+ int.conj
+ float_conj: |
+ flo.conj
+ integer_numerator: |
+ int.numerator
+ integer_denominator: |
+ int.denominator
+loop_count: 20000000
diff --git a/benchmark/object_allocate.yml b/benchmark/object_allocate.yml
new file mode 100644
index 0000000000..93ff463e41
--- /dev/null
+++ b/benchmark/object_allocate.yml
@@ -0,0 +1,21 @@
+prelude: |
+ class Eight
+ 8.times { include(Module.new) }
+ end
+ class ThirtyTwo
+ 32.times { include(Module.new) }
+ end
+ class SixtyFour
+ 64.times { include(Module.new) }
+ end
+ class OneTwentyEight
+ 128.times { include(Module.new) }
+ end
+ # Disable GC to see raw throughput:
+ GC.disable
+benchmark:
+ allocate_8_deep: Eight.new
+ allocate_32_deep: ThirtyTwo.new
+ allocate_64_deep: SixtyFour.new
+ allocate_128_deep: OneTwentyEight.new
+loop_count: 100000
diff --git a/benchmark/objspace_dump_all.yml b/benchmark/objspace_dump_all.yml
new file mode 100644
index 0000000000..ebab562d2e
--- /dev/null
+++ b/benchmark/objspace_dump_all.yml
@@ -0,0 +1,13 @@
+prelude: |
+ require 'objspace'
+ require 'tempfile'
+ $objs = 1_000.times.map { Object.new }
+ $strings = 1_000.times.map { |i| "string #{i}" }
+ $file = Tempfile.new('heap')
+ $dev_null = File.open(File::NULL, 'w+')
+
+benchmark:
+ dump_all_string: "ObjectSpace.dump_all(output: :string)"
+ dump_all_file: "ObjectSpace.dump_all(output: $file)"
+ dump_all_dev_null: "ObjectSpace.dump_all(output: $dev_null)"
+loop_count: 1
diff --git a/benchmark/other-lang/fact.py b/benchmark/other-lang/fact.py
index 01593965d9..1ce9f76275 100644
--- a/benchmark/other-lang/fact.py
+++ b/benchmark/other-lang/fact.py
@@ -3,7 +3,7 @@
def factL(n):
r = 1
- for x in range(2, n):
+ for x in range(2, n+1):
r *= x
return r
diff --git a/benchmark/pm_array.yml b/benchmark/pm_array.yml
new file mode 100644
index 0000000000..babb65a289
--- /dev/null
+++ b/benchmark/pm_array.yml
@@ -0,0 +1,19 @@
+prelude: |
+ def call(*val)
+ case val
+ in [String => body]
+ [200, {}, [body]]
+ in [Integer => status]
+ [status, {}, [""]]
+ in [Integer, String] => response
+ [response[0], {}, [response[1]]]
+ in [Integer, Hash, String] => response
+ [response[0], response[1], [response[2]]]
+ end
+ end
+
+benchmark:
+ first_match: call("ok")
+ second_match: call(401)
+ third_match: call(200, "ok")
+ fourth_match: call(201, {}, "created")
diff --git a/benchmark/ractor_const.yml b/benchmark/ractor_const.yml
new file mode 100644
index 0000000000..d7ab74bdca
--- /dev/null
+++ b/benchmark/ractor_const.yml
@@ -0,0 +1,4 @@
+type: lib/benchmark_driver/runner/ractor
+benchmark:
+ ractor_const: Object
+ractor: 1
diff --git a/benchmark/ractor_float_to_s.yml b/benchmark/ractor_float_to_s.yml
new file mode 100644
index 0000000000..8f492be668
--- /dev/null
+++ b/benchmark/ractor_float_to_s.yml
@@ -0,0 +1,8 @@
+type: lib/benchmark_driver/runner/ractor
+prelude: |
+ FLOATS = [*0.0.step(1.0, 0.001)]
+benchmark:
+ ractor_float_to_s: |
+ FLOATS.each {|f| f.to_s}
+loop_count: 100
+ractor: 2
diff --git a/benchmark/range_last.yml b/benchmark/range_last.yml
new file mode 100644
index 0000000000..a6674f82ee
--- /dev/null
+++ b/benchmark/range_last.yml
@@ -0,0 +1,4 @@
+benchmark:
+ - (1..1_000_000).last(100)
+ - (1..1_000_000).last(1000)
+ - (1..1_000_000).last(10000)
diff --git a/benchmark/range_min.yml b/benchmark/range_min.yml
new file mode 100644
index 0000000000..9e60dd7308
--- /dev/null
+++ b/benchmark/range_min.yml
@@ -0,0 +1,2 @@
+benchmark:
+ - (1..10).min
diff --git a/benchmark/realpath.yml b/benchmark/realpath.yml
new file mode 100644
index 0000000000..90a029d5b9
--- /dev/null
+++ b/benchmark/realpath.yml
@@ -0,0 +1,30 @@
+prelude: |
+ f = File
+ pwd = Dir.pwd
+ Dir.mkdir('b') unless f.directory?('b')
+ f.write('b/a', '') unless f.file?('b/a')
+
+ relative = 'b/a'
+ absolute = File.join(pwd, relative)
+ dir = 'b'
+ file = 'a'
+
+ relative_dir = 'b/c'
+ absolute_dir = File.join(pwd, relative_dir)
+ file_dir = 'c'
+benchmark:
+ relative_nil: "f.realpath(relative, nil)"
+ absolute_nil: "f.realpath(absolute, nil)"
+ relative_relative: "f.realpath(file, dir)"
+ absolute_relative: "f.realpath(absolute, dir)"
+ relative_absolute: "f.realpath(relative, pwd)"
+ relative_nil_dir: "f.realdirpath(relative_dir, nil)"
+ absolute_nil_dir: "f.realdirpath(absolute_dir, nil)"
+ relative_relative_dir: "f.realdirpath(file_dir, dir)"
+ absolute_relative_dir: "f.realdirpath(absolute_dir, dir)"
+ relative_absolute_dir: "f.realdirpath(relative_dir, pwd)"
+ relative_nil_notexist: "f.realpath(relative_dir, nil) rescue nil"
+ absolute_nil_notexist: "f.realpath(absolute_dir, nil) rescue nil"
+ relative_relative_notexist: "f.realpath(file_dir, dir) rescue nil"
+ absolute_relative_notexist: "f.realpath(absolute_dir, dir) rescue nil"
+ relative_absolute_notexist: "f.realpath(relative_dir, pwd) rescue nil"
diff --git a/benchmark/require.yml b/benchmark/require.yml
index 711d8e11e9..09f218cf08 100644
--- a/benchmark/require.yml
+++ b/benchmark/require.yml
@@ -15,11 +15,7 @@ prelude: |
FileUtils.mkdir_p(data_dir)
1.upto(num_files) do |i|
- f = File.open("#{data_dir}/c#{i}.rb", "w")
- f.puts <<-END
- class C#{i}
- end
- END
+ File.write("#{data_dir}/c#{i}.rb", "class C#{i}\n""end\n")
end
end
diff --git a/benchmark/require_thread.yml b/benchmark/require_thread.yml
index 87e0ba888b..0c63257106 100644
--- a/benchmark/require_thread.yml
+++ b/benchmark/require_thread.yml
@@ -15,11 +15,7 @@ prelude: |
FileUtils.mkdir_p(data_dir)
1.upto(num_files) do |i|
- f = File.open("#{data_dir}/c#{i}.rb", "w")
- f.puts <<-END
- class C#{i}
- end
- END
+ File.write("#{data_dir}/c#{i}.rb", "class C#{i}\n""end\n")
end
end
diff --git a/benchmark/so_ackermann.rb b/benchmark/so_ackermann.rb
index 7db5be9050..4effa1ecaf 100644
--- a/benchmark/so_ackermann.rb
+++ b/benchmark/so_ackermann.rb
@@ -1,5 +1,5 @@
#!/usr/bin/ruby
-# -*- mode: ruby -*-
+# -*- Ruby -*-
# $Id: ackermann-ruby.code,v 1.4 2004/11/13 07:40:41 bfulgham Exp $
# http://www.bagley.org/~doug/shootout/
diff --git a/benchmark/so_array.rb b/benchmark/so_array.rb
index 2b8fce8f99..767e03db5f 100644
--- a/benchmark/so_array.rb
+++ b/benchmark/so_array.rb
@@ -1,5 +1,5 @@
#!/usr/bin/ruby
-# -*- mode: ruby -*-
+# -*- Ruby -*-
# $Id: ary-ruby.code,v 1.4 2004/11/13 07:41:27 bfulgham Exp $
# http://www.bagley.org/~doug/shootout/
# with help from Paul Brannan and Mark Hubbart
diff --git a/benchmark/so_concatenate.rb b/benchmark/so_concatenate.rb
index 873214de7c..4468e20ac8 100644
--- a/benchmark/so_concatenate.rb
+++ b/benchmark/so_concatenate.rb
@@ -1,5 +1,5 @@
#!/usr/bin/ruby
-# -*- mode: ruby -*-
+# -*- Ruby -*-
# $Id: strcat-ruby.code,v 1.4 2004/11/13 07:43:28 bfulgham Exp $
# http://www.bagley.org/~doug/shootout/
# based on code from Aristarkh A Zagorodnikov and Dat Nguyen
diff --git a/benchmark/so_count_words.yml b/benchmark/so_count_words.yml
index d0a6c8dd3e..99683505f9 100644
--- a/benchmark/so_count_words.yml
+++ b/benchmark/so_count_words.yml
@@ -1,6 +1,5 @@
prelude: |
#!/usr/bin/ruby
- # -*- mode: ruby -*-
wc_input_base = <<EOS
Subject: Re: Who was Izchak Miller?
diff --git a/benchmark/so_exception.rb b/benchmark/so_exception.rb
index deb003a594..eb205b4df1 100644
--- a/benchmark/so_exception.rb
+++ b/benchmark/so_exception.rb
@@ -1,5 +1,5 @@
#!/usr/bin/ruby
-# -*- mode: ruby -*-
+# -*- Ruby -*-
# $Id: except-ruby.code,v 1.4 2004/11/13 07:41:33 bfulgham Exp $
# http://www.bagley.org/~doug/shootout/
diff --git a/benchmark/so_matrix.rb b/benchmark/so_matrix.rb
index e2c5c8e559..2d1e72bda9 100644
--- a/benchmark/so_matrix.rb
+++ b/benchmark/so_matrix.rb
@@ -1,5 +1,5 @@
#!/usr/bin/ruby
-# -*- mode: ruby -*-
+# -*- Ruby -*-
# $Id: matrix-ruby.code,v 1.4 2004/11/13 07:42:14 bfulgham Exp $
# http://www.bagley.org/~doug/shootout/
diff --git a/benchmark/so_nbody.rb b/benchmark/so_nbody.rb
index d6c5bb9e61..9884fc4edc 100644
--- a/benchmark/so_nbody.rb
+++ b/benchmark/so_nbody.rb
@@ -12,38 +12,38 @@ def _puts *args
end
class Planet
- attr_accessor :x, :y, :z, :vx, :vy, :vz, :mass
+ attr_accessor :x, :y, :z, :vx, :vy, :vz, :mass
- def initialize(x, y, z, vx, vy, vz, mass)
- @x, @y, @z = x, y, z
- @vx, @vy, @vz = vx * DAYS_PER_YEAR, vy * DAYS_PER_YEAR, vz * DAYS_PER_YEAR
- @mass = mass * SOLAR_MASS
- end
-
- def move_from_i(bodies, nbodies, dt, i)
- while i < nbodies
- b2 = bodies[i]
- dx = @x - b2.x
- dy = @y - b2.y
- dz = @z - b2.z
-
- distance = Math.sqrt(dx * dx + dy * dy + dz * dz)
- mag = dt / (distance * distance * distance)
- b_mass_mag, b2_mass_mag = @mass * mag, b2.mass * mag
-
- @vx -= dx * b2_mass_mag
- @vy -= dy * b2_mass_mag
- @vz -= dz * b2_mass_mag
- b2.vx += dx * b_mass_mag
- b2.vy += dy * b_mass_mag
- b2.vz += dz * b_mass_mag
- i += 1
+ def initialize(x, y, z, vx, vy, vz, mass)
+ @x, @y, @z = x, y, z
+ @vx, @vy, @vz = vx * DAYS_PER_YEAR, vy * DAYS_PER_YEAR, vz * DAYS_PER_YEAR
+ @mass = mass * SOLAR_MASS
end
- @x += dt * @vx
- @y += dt * @vy
- @z += dt * @vz
- end
+ def move_from_i(bodies, nbodies, dt, i)
+ while i < nbodies
+ b2 = bodies[i]
+ dx = @x - b2.x
+ dy = @y - b2.y
+ dz = @z - b2.z
+
+ distance = Math.sqrt(dx * dx + dy * dy + dz * dz)
+ mag = dt / (distance * distance * distance)
+ b_mass_mag, b2_mass_mag = @mass * mag, b2.mass * mag
+
+ @vx -= dx * b2_mass_mag
+ @vy -= dy * b2_mass_mag
+ @vz -= dz * b2_mass_mag
+ b2.vx += dx * b_mass_mag
+ b2.vy += dy * b_mass_mag
+ b2.vz += dz * b_mass_mag
+ i += 1
+ end
+
+ @x += dt * @vx
+ @y += dt * @vy
+ @z += dt * @vz
+ end
end
def energy(bodies)
diff --git a/benchmark/so_nested_loop.rb b/benchmark/so_nested_loop.rb
index a0513f8c47..766fcf7b84 100644
--- a/benchmark/so_nested_loop.rb
+++ b/benchmark/so_nested_loop.rb
@@ -1,5 +1,5 @@
#!/usr/bin/ruby
-# -*- mode: ruby -*-
+# -*- Ruby -*-
# $Id: nestedloop-ruby.code,v 1.4 2004/11/13 07:42:22 bfulgham Exp $
# http://www.bagley.org/~doug/shootout/
# from Avi Bryant
diff --git a/benchmark/so_object.rb b/benchmark/so_object.rb
index e8607c7199..131f44624c 100644
--- a/benchmark/so_object.rb
+++ b/benchmark/so_object.rb
@@ -1,5 +1,5 @@
#!/usr/bin/ruby
-# -*- mode: ruby -*-
+# -*- Ruby -*-
# $Id: objinst-ruby.code,v 1.4 2004/11/13 07:42:25 bfulgham Exp $
# http://www.bagley.org/~doug/shootout/
# with help from Aristarkh Zagorodnikov
diff --git a/benchmark/string_capitalize.yml b/benchmark/string_capitalize.yml
new file mode 100644
index 0000000000..7d23fd3d35
--- /dev/null
+++ b/benchmark/string_capitalize.yml
@@ -0,0 +1,10 @@
+prelude: |
+ str1 = [*"a".."m",*"N".."Z",*"0".."9"].join("")
+ str10 = str1 * 10
+ str100 = str10 * 10
+ str1000 = str100 * 10
+benchmark:
+ capitalize-1: str1.capitalize
+ capitalize-10: str10.capitalize
+ capitalize-100: str100.capitalize
+ capitalize-1000: str1000.capitalize
diff --git a/benchmark/string_casecmp.yml b/benchmark/string_casecmp.yml
new file mode 100644
index 0000000000..2354040a04
--- /dev/null
+++ b/benchmark/string_casecmp.yml
@@ -0,0 +1,26 @@
+prelude: |
+ lstr1 = [*"a".."z",*"0".."9"].join("")
+ lstr10 = lstr1 * 10
+ lstr100 = lstr10 * 10
+ lstr1000 = lstr100 * 10
+ lnonascii1 = [*"\u{e0}".."\u{ff}"].join("")
+ lnonascii10 = lnonascii1 * 10
+ lnonascii100 = lnonascii10 * 10
+ lnonascii1000 = lnonascii100 * 10
+ ustr1 = [*"A".."Z",*"0".."9"].join("")
+ ustr10 = ustr1 * 10
+ ustr100 = ustr10 * 10
+ ustr1000 = ustr100 * 10
+ unonascii1 = [*"\u{c0}".."\u{df}"].join("")
+ unonascii10 = unonascii1 * 10
+ unonascii100 = unonascii10 * 10
+ unonascii1000 = unonascii100 * 10
+benchmark:
+ casecmp-1: lstr1.casecmp(ustr1)
+ casecmp-10: lstr10.casecmp(ustr10)
+ casecmp-100: lstr100.casecmp(ustr100)
+ casecmp-1000: lstr1000.casecmp(ustr1000)
+ casecmp-nonascii1: lnonascii1.casecmp(unonascii1)
+ casecmp-nonascii10: lnonascii10.casecmp(unonascii10)
+ casecmp-nonascii100: lnonascii100.casecmp(unonascii100)
+ casecmp-nonascii1000: lnonascii1000.casecmp(unonascii1000)
diff --git a/benchmark/string_casecmp_p.yml b/benchmark/string_casecmp_p.yml
new file mode 100644
index 0000000000..a790ce7d55
--- /dev/null
+++ b/benchmark/string_casecmp_p.yml
@@ -0,0 +1,26 @@
+prelude: |
+ lstr1 = [*"a".."z",*"0".."9"].join("")
+ lstr10 = lstr1 * 10
+ lstr100 = lstr10 * 10
+ lstr1000 = lstr100 * 10
+ lnonascii1 = [*"\u{e0}".."\u{ff}"].join("")
+ lnonascii10 = lnonascii1 * 10
+ lnonascii100 = lnonascii10 * 10
+ lnonascii1000 = lnonascii100 * 10
+ ustr1 = [*"A".."Z",*"0".."9"].join("")
+ ustr10 = ustr1 * 10
+ ustr100 = ustr10 * 10
+ ustr1000 = ustr100 * 10
+ unonascii1 = [*"\u{c0}".."\u{df}"].join("")
+ unonascii10 = unonascii1 * 10
+ unonascii100 = unonascii10 * 10
+ unonascii1000 = unonascii100 * 10
+benchmark:
+ casecmp_p-1: lstr1.casecmp?(ustr1)
+ casecmp_p-10: lstr10.casecmp?(ustr10)
+ casecmp_p-100: lstr100.casecmp?(ustr100)
+ casecmp_p-1000: lstr1000.casecmp?(ustr1000)
+ casecmp_p-nonascii1: lnonascii1.casecmp?(unonascii1)
+ casecmp_p-nonascii10: lnonascii10.casecmp?(unonascii10)
+ casecmp_p-nonascii100: lnonascii100.casecmp?(unonascii100)
+ casecmp_p-nonascii1000: lnonascii1000.casecmp?(unonascii1000)
diff --git a/benchmark/string_concat.yml b/benchmark/string_concat.yml
new file mode 100644
index 0000000000..e65c00cca9
--- /dev/null
+++ b/benchmark/string_concat.yml
@@ -0,0 +1,45 @@
+prelude: |
+ CHUNK = "a" * 64
+ UCHUNK = "é" * 32
+ GC.disable # GC causes a lot of variance
+benchmark:
+ binary_concat_7bit: |
+ buffer = String.new(capacity: 4096, encoding: Encoding::BINARY)
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ utf8_concat_7bit: |
+ buffer = String.new(capacity: 4096, encoding: Encoding::UTF_8)
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ buffer << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK << CHUNK
+ utf8_concat_UTF8: |
+ buffer = String.new(capacity: 4096, encoding: Encoding::UTF_8)
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ buffer << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK << UCHUNK
+ interpolation: |
+ buffer = "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}" \
+ "#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}#{CHUNK}"
diff --git a/benchmark/string_downcase.yml b/benchmark/string_downcase.yml
new file mode 100644
index 0000000000..1fea6afbec
--- /dev/null
+++ b/benchmark/string_downcase.yml
@@ -0,0 +1,18 @@
+prelude: |
+ str1 = [*"A".."Z",*"0".."9"].join("")
+ str10 = str1 * 10
+ str100 = str10 * 10
+ str1000 = str100 * 10
+ nonascii1 = [*"\u{c0}".."\u{df}"].join("")
+ nonascii10 = nonascii1 * 10
+ nonascii100 = nonascii10 * 10
+ nonascii1000 = nonascii100 * 10
+benchmark:
+ downcase-1: str1.upcase
+ downcase-10: str10.upcase
+ downcase-100: str100.upcase
+ downcase-1000: str1000.upcase
+ downcase-nonascii1: nonascii1.downcase
+ downcase-nonascii10: nonascii10.downcase
+ downcase-nonascii100: nonascii100.downcase
+ downcase-nonascii1000: nonascii1000.downcase
diff --git a/benchmark/string_slice.yml b/benchmark/string_slice.yml
new file mode 100644
index 0000000000..fc2393c5d1
--- /dev/null
+++ b/benchmark/string_slice.yml
@@ -0,0 +1,11 @@
+prelude: |
+ long_string = "x"*1000+"-hår"
+benchmark:
+ regexp-short: |
+ "x-hår".slice!(/-(.)(.)(.)/, 3)
+ regexp-long: |
+ long_string.dup.slice!(/-(.)(.)(.)/, 3)
+ string-short: |
+ "x-hår".slice!("r")
+ string-long: |
+ long_string.dup.slice!("r")
diff --git a/benchmark/string_split.yml b/benchmark/string_split.yml
new file mode 100644
index 0000000000..cc2c7d7855
--- /dev/null
+++ b/benchmark/string_split.yml
@@ -0,0 +1,22 @@
+prelude: |
+ str1 = [*0..5].join(" ") + " "
+ str10 = str1 * 10
+ str100 = str10 * 10
+ str1000 = str100 * 10
+benchmark:
+ to_chars-1: str1.split('')
+ to_chars-10: str10.split('')
+ to_chars-100: str100.split('')
+ to_chars-1000: str1000.split('')
+ to_words-1: str1.split(' ')
+ to_words-10: str10.split(' ')
+ to_words-100: str100.split(' ')
+ to_words-1000: str1000.split(' ')
+ re_chars-1: str1.split(//)
+ re_chars-10: str10.split(//)
+ re_chars-100: str100.split(//)
+ re_chars-1000: str1000.split(//)
+ re_space-1: str1.split(/ /)
+ re_space-10: str10.split(/ /)
+ re_space-100: str100.split(/ /)
+ re_space-1000: str1000.split(/ /)
diff --git a/benchmark/string_swapcase.yml b/benchmark/string_swapcase.yml
new file mode 100644
index 0000000000..eeb5928907
--- /dev/null
+++ b/benchmark/string_swapcase.yml
@@ -0,0 +1,18 @@
+prelude: |
+ str1 = [*"A".."M",*"n".."z",*"0".."9"].join("")
+ str10 = str1 * 10
+ str100 = str10 * 10
+ str1000 = str100 * 10
+ nonascii1 = [*"\u{c0}".."\u{cf}",*"\u{f0}".."\u{ff}"].join("")
+ nonascii10 = nonascii1 * 10
+ nonascii100 = nonascii10 * 10
+ nonascii1000 = nonascii100 * 10
+benchmark:
+ swapcase-1: str1.swapcase
+ swapcase-10: str10.swapcase
+ swapcase-100: str100.swapcase
+ swapcase-1000: str1000.swapcase
+ swapcase-nonascii1: nonascii1.swapcase
+ swapcase-nonascii10: nonascii10.swapcase
+ swapcase-nonascii100: nonascii100.swapcase
+ swapcase-nonascii1000: nonascii1000.swapcase
diff --git a/benchmark/string_upcase.yml b/benchmark/string_upcase.yml
new file mode 100644
index 0000000000..dab84bbde2
--- /dev/null
+++ b/benchmark/string_upcase.yml
@@ -0,0 +1,18 @@
+prelude: |
+ str1 = [*"a".."z",*"0".."9"].join("")
+ str10 = str1 * 10
+ str100 = str10 * 10
+ str1000 = str100 * 10
+ nonascii1 = [*"\u{e0}".."\u{ff}"].join("")
+ nonascii10 = nonascii1 * 10
+ nonascii100 = nonascii10 * 10
+ nonascii1000 = nonascii100 * 10
+benchmark:
+ upcase-1: str1.upcase
+ upcase-10: str10.upcase
+ upcase-100: str100.upcase
+ upcase-1000: str1000.upcase
+ upcase-nonascii1: nonascii1.upcase
+ upcase-nonascii10: nonascii10.upcase
+ upcase-nonascii100: nonascii100.upcase
+ upcase-nonascii1000: nonascii1000.upcase
diff --git a/benchmark/time_at.yml b/benchmark/time_at.yml
new file mode 100644
index 0000000000..3247efbe77
--- /dev/null
+++ b/benchmark/time_at.yml
@@ -0,0 +1,7 @@
+prelude: |
+ # frozen_string_literal: true
+benchmark:
+ - 'Time.at(0)'
+ - 'Time.at(0, 500)'
+ - 'Time.at(0, in: "+09:00")'
+ - 'Time.at(0, 500, in: "+09:00")'
diff --git a/benchmark/time_new.yml b/benchmark/time_new.yml
new file mode 100644
index 0000000000..5947dd3a41
--- /dev/null
+++ b/benchmark/time_new.yml
@@ -0,0 +1,4 @@
+benchmark:
+ - 'Time.new(2021)'
+ - 'Time.new(2021, 8, 22)'
+ - 'Time.new(2021, 8, 22, in: "+09:00")'
diff --git a/benchmark/time_now.yml b/benchmark/time_now.yml
new file mode 100644
index 0000000000..f6d6a31489
--- /dev/null
+++ b/benchmark/time_now.yml
@@ -0,0 +1,3 @@
+benchmark:
+ - 'Time.now'
+ - 'Time.now(in: "+09:00")'
diff --git a/benchmark/time_parse.yml b/benchmark/time_parse.yml
new file mode 100644
index 0000000000..6060b58bc6
--- /dev/null
+++ b/benchmark/time_parse.yml
@@ -0,0 +1,10 @@
+prelude: |
+ require 'time'
+ inspect = "2021-08-23 09:57:02 +0900"
+ iso8601 = "2021-08-23T09:57:02+09:00"
+benchmark:
+ - Time.iso8601(iso8601)
+ - Time.parse(iso8601)
+ - Time.parse(inspect)
+ - Time.new(iso8601) rescue Time.iso8601(iso8601)
+ - Time.new(inspect) rescue Time.parse(inspect)
diff --git a/benchmark/time_strptime.yml b/benchmark/time_strptime.yml
new file mode 100644
index 0000000000..8d89ebb7a7
--- /dev/null
+++ b/benchmark/time_strptime.yml
@@ -0,0 +1,13 @@
+prelude: |
+ require 'time'
+benchmark:
+ - Time.strptime("28/Aug/2005:06:54:20 +0000", "%d/%b/%Y:%T %z")
+ - Time.strptime("1", "%s")
+ - Time.strptime("0 +0100", "%s %z")
+ - Time.strptime("0 UTC", "%s %z")
+ - Time.strptime("1.5", "%s.%N")
+ - Time.strptime("1.000000000001", "%s.%N")
+ - Time.strptime("20010203 -0200", "%Y%m%d %z")
+ - Time.strptime("20010203 UTC", "%Y%m%d %z")
+ - Time.strptime("2018-365", "%Y-%j")
+ - Time.strptime("2018-091", "%Y-%j")
diff --git a/benchmark/vm1_attr_ivar.yml b/benchmark/vm1_attr_ivar.yml
deleted file mode 100644
index f714dd9bd9..0000000000
--- a/benchmark/vm1_attr_ivar.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-prelude: |
- class C
- attr_reader :a, :b
- def initialize
- @a = nil
- @b = nil
- end
- end
- obj = C.new
-benchmark:
- vm1_attr_ivar: |
- j = obj.a
- k = obj.b
-loop_count: 30000000
diff --git a/benchmark/vm1_attr_ivar_set.yml b/benchmark/vm1_attr_ivar_set.yml
deleted file mode 100644
index f383e59ef4..0000000000
--- a/benchmark/vm1_attr_ivar_set.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-prelude: |
- class C
- attr_accessor :a, :b
- def initialize
- @a = nil
- @b = nil
- end
- end
- obj = C.new
-benchmark:
- vm1_attr_ivar_set: |
- obj.a = 1
- obj.b = 2
-loop_count: 30000000
diff --git a/benchmark/vm1_block.yml b/benchmark/vm1_block.yml
deleted file mode 100644
index ac7c940f93..0000000000
--- a/benchmark/vm1_block.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-prelude: |
- def m
- yield
- end
-benchmark:
- vm1_block: |
- m{
- }
-loop_count: 30000000
diff --git a/benchmark/vm1_blockparam.yml b/benchmark/vm1_blockparam.yml
deleted file mode 100644
index 947b8c53d5..0000000000
--- a/benchmark/vm1_blockparam.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- def m &b
- end
-benchmark:
- vm1_blockparam: |
- m{}
-loop_count: 30000000
diff --git a/benchmark/vm1_blockparam_call.yml b/benchmark/vm1_blockparam_call.yml
deleted file mode 100644
index e2817a3ce2..0000000000
--- a/benchmark/vm1_blockparam_call.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- def m &b
- b.call
- end
-benchmark:
- vm1_blockparam_call: |
- m{}
-loop_count: 30000000
diff --git a/benchmark/vm1_blockparam_pass.yml b/benchmark/vm1_blockparam_pass.yml
deleted file mode 100644
index ca1bef3369..0000000000
--- a/benchmark/vm1_blockparam_pass.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-prelude: |
- def bp_yield
- yield
- end
-
- def bp_pass &b
- bp_yield &b
- end
-benchmark:
- vm1_blockparam_pass: |
- bp_pass{}
-loop_count: 30000000
diff --git a/benchmark/vm1_blockparam_yield.yml b/benchmark/vm1_blockparam_yield.yml
deleted file mode 100644
index 56ae617798..0000000000
--- a/benchmark/vm1_blockparam_yield.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- def bp_yield &b
- yield
- end
-benchmark:
- vm1_blockparam_yield: |
- bp_yield{}
-loop_count: 30000000
diff --git a/benchmark/vm1_const.yml b/benchmark/vm1_const.yml
deleted file mode 100644
index b98db1545c..0000000000
--- a/benchmark/vm1_const.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- Const = 1
-benchmark:
- vm1_const: |
- j = Const
- k = Const
-loop_count: 30000000
diff --git a/benchmark/vm1_ensure.yml b/benchmark/vm1_ensure.yml
deleted file mode 100644
index afbbe38bec..0000000000
--- a/benchmark/vm1_ensure.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-# Not utilizing loop_count since using it for this is too unstable for now
-benchmark:
- vm1_ensure: |
- i = 0
- while i<30_000_000
- i += 1
- begin
- begin
- ensure
- end
- ensure
- end
- end
-loop_count: 1
diff --git a/benchmark/vm1_float_simple.yml b/benchmark/vm1_float_simple.yml
deleted file mode 100644
index 4e9ad1852b..0000000000
--- a/benchmark/vm1_float_simple.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- f = 0.0
-benchmark:
- vm1_float_simple: |
- f += 0.1; f -= 0.1
- f += 0.1; f -= 0.1
- f += 0.1; f -= 0.1
-loop_count: 30000000
diff --git a/benchmark/vm1_gc_short_lived.yml b/benchmark/vm1_gc_short_lived.yml
deleted file mode 100644
index 8fdcb7371d..0000000000
--- a/benchmark/vm1_gc_short_lived.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-benchmark:
- vm1_gc_short_lived: |
- a = '' # short-lived String
- b = ''
- c = ''
- d = ''
- e = ''
- f = ''
-loop_count: 30000000
diff --git a/benchmark/vm1_gc_short_with_complex_long.yml b/benchmark/vm1_gc_short_with_complex_long.yml
deleted file mode 100644
index c22ea74a60..0000000000
--- a/benchmark/vm1_gc_short_with_complex_long.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-prelude: |
- def nested_hash h, n
- if n == 0
- ''
- else
- 10.times{
- h[Object.new] = nested_hash(h, n-1)
- }
- end
- end
-
- long_lived = Hash.new
- nested_hash long_lived, 6
-
- GC.start
- GC.start
-benchmark:
- vm1_gc_short_with_complex_long: |
- a = '' # short-lived String
- b = ''
- c = ''
- d = ''
- e = ''
- f = ''
-loop_count: 30000000
diff --git a/benchmark/vm1_gc_short_with_long.yml b/benchmark/vm1_gc_short_with_long.yml
deleted file mode 100644
index c731aae548..0000000000
--- a/benchmark/vm1_gc_short_with_long.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-prelude: |
- long_lived = Array.new(1_000_000){|i| "#{i}"}
- GC.start
- GC.start
-benchmark:
- vm1_gc_short_with_long: |
- a = '' # short-lived String
- b = ''
- c = ''
- d = ''
- e = ''
- f = ''
-loop_count: 30000000
diff --git a/benchmark/vm1_gc_short_with_symbol.yml b/benchmark/vm1_gc_short_with_symbol.yml
deleted file mode 100644
index 7fc1abedd8..0000000000
--- a/benchmark/vm1_gc_short_with_symbol.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-prelude: |
- 50_000.times{|i| sym = "sym#{i}".to_sym}
- GC.start
- GC.start
-benchmark:
- vm1_gc_short_with_symbol: |
- a = '' # short-lived String
- b = ''
- c = ''
- d = ''
- e = ''
- f = ''
-loop_count: 30000000
diff --git a/benchmark/vm1_gc_wb_ary.yml b/benchmark/vm1_gc_wb_ary.yml
deleted file mode 100644
index 50fb4b6f84..0000000000
--- a/benchmark/vm1_gc_wb_ary.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-prelude: |
- short_lived_ary = []
-
- if RUBY_VERSION >= "2.2.0"
- GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true)
- end
-
- short_lived = ''
-benchmark:
- vm1_gc_wb_ary: |
- short_lived_ary[0] = short_lived # write barrier
-loop_count: 30000000
diff --git a/benchmark/vm1_gc_wb_ary_promoted.yml b/benchmark/vm1_gc_wb_ary_promoted.yml
deleted file mode 100644
index cf9b5de005..0000000000
--- a/benchmark/vm1_gc_wb_ary_promoted.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-prelude: |
- long_lived = []
-
- if RUBY_VERSION > "2.2.0"
- 3.times{ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) }
- elsif
- GC.start
- end
-
- short_lived = ''
-
-benchmark:
- vm1_gc_wb_ary_promoted: |
- long_lived[0] = short_lived # write barrier
-loop_count: 30000000
diff --git a/benchmark/vm1_gc_wb_obj.yml b/benchmark/vm1_gc_wb_obj.yml
deleted file mode 100644
index 9dc08e7e1a..0000000000
--- a/benchmark/vm1_gc_wb_obj.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-prelude: |
- class C
- attr_accessor :foo
- end
- short_lived_obj = C.new
-
- if RUBY_VERSION >= "2.2.0"
- GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true)
- end
-
- short_lived = ''
-benchmark:
- vm1_gc_wb_obj: |
- short_lived_obj.foo = short_lived # write barrier
-loop_count: 30000000
diff --git a/benchmark/vm1_gc_wb_obj_promoted.yml b/benchmark/vm1_gc_wb_obj_promoted.yml
deleted file mode 100644
index 26859d2a52..0000000000
--- a/benchmark/vm1_gc_wb_obj_promoted.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-prelude: |
- class C
- attr_accessor :foo
- end
- long_lived = C.new
-
- if RUBY_VERSION >= "2.2.0"
- 3.times{ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) }
- elsif
- GC.start
- end
-
- short_lived = ''
-benchmark:
- vm1_gc_wb_obj_promoted: |
- long_lived.foo = short_lived # write barrier
-loop_count: 30000000
diff --git a/benchmark/vm1_ivar.yml b/benchmark/vm1_ivar.yml
deleted file mode 100644
index 7aa6fac729..0000000000
--- a/benchmark/vm1_ivar.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-prelude: "@a = 1\n"
-benchmark:
- vm1_ivar: |
- j = @a
- k = @a
-loop_count: 30000000
diff --git a/benchmark/vm1_ivar_set.yml b/benchmark/vm1_ivar_set.yml
deleted file mode 100644
index 6f19412d16..0000000000
--- a/benchmark/vm1_ivar_set.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-benchmark:
- vm1_ivar_set: |
- @a = 1
- @b = 2
-loop_count: 30000000
diff --git a/benchmark/vm1_length.yml b/benchmark/vm1_length.yml
deleted file mode 100644
index a18e2ca2e6..0000000000
--- a/benchmark/vm1_length.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- a = 'abc'
- b = [1, 2, 3]
-benchmark:
- vm1_length: |
- a.length
- b.length
-loop_count: 30000000
diff --git a/benchmark/vm1_lvar_init.yml b/benchmark/vm1_lvar_init.yml
deleted file mode 100644
index 10e2becef9..0000000000
--- a/benchmark/vm1_lvar_init.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-# while loop cost is not removed because `i` is used in the script
-benchmark:
- vm1_lvar_init: |
- def m v
- unless v
- # unreachable code
- v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v9 = v10 =
- v11 = v12 = v13 = v14 = v15 = v16 = v17 = v18 = v19 = v20 =
- v21 = v22 = v23 = v24 = v25 = v26 = v27 = v28 = v29 = v30 =
- v31 = v32 = v33 = v34 = v35 = v36 = v37 = v38 = v39 = v40 =
- v41 = v42 = v43 = v44 = v45 = v46 = v47 = v48 = v49 = v50 = 1
- end
- end
-
- i = 0
-
- while i<30_000_000
- i += 1
- m i
- end
-loop_count: 1
diff --git a/benchmark/vm1_lvar_set.yml b/benchmark/vm1_lvar_set.yml
deleted file mode 100644
index df8f6b6ea4..0000000000
--- a/benchmark/vm1_lvar_set.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-benchmark:
- vm1_lvar_set: |
- a = b = c = d = e = f = g = h = j = k = l = m = n = o = p = q = r = 1
-loop_count: 30000000
diff --git a/benchmark/vm1_neq.yml b/benchmark/vm1_neq.yml
deleted file mode 100644
index 65a8128dda..0000000000
--- a/benchmark/vm1_neq.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- obj1 = Object.new
- obj2 = Object.new
-benchmark:
- vm1_neq: |
- obj1 != obj2
-loop_count: 30000000
diff --git a/benchmark/vm1_not.yml b/benchmark/vm1_not.yml
deleted file mode 100644
index 0fb7b282a9..0000000000
--- a/benchmark/vm1_not.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-prelude: |
- obj = Object.new
-benchmark:
- vm1_not: |
- !obj
-loop_count: 30000000
diff --git a/benchmark/vm1_rescue.yml b/benchmark/vm1_rescue.yml
deleted file mode 100644
index a175b823af..0000000000
--- a/benchmark/vm1_rescue.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-benchmark:
- vm1_rescue: |
- begin
- rescue
- end
-loop_count: 30000000
diff --git a/benchmark/vm1_simplereturn.yml b/benchmark/vm1_simplereturn.yml
deleted file mode 100644
index 3564aac7e2..0000000000
--- a/benchmark/vm1_simplereturn.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- def m
- return 1
- end
-benchmark:
- vm1_simplereturn: m
-loop_count: 30000000
diff --git a/benchmark/vm1_swap.yml b/benchmark/vm1_swap.yml
deleted file mode 100644
index fed87ccd62..0000000000
--- a/benchmark/vm1_swap.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- a = 1
- b = 2
-benchmark:
- vm1_swap: |
- a, b = b, a
-loop_count: 30000000
diff --git a/benchmark/vm1_yield.yml b/benchmark/vm1_yield.yml
deleted file mode 100644
index ae1f9316f9..0000000000
--- a/benchmark/vm1_yield.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-# while loop cost is not removed due to benchmark_driver.gem's limitation
-benchmark:
- vm1_yield: |
- def m
- i = 0
- while i<30_000_000
- i += 1
- yield
- end
- end
-
- m{}
-loop_count: 1
diff --git a/benchmark/vm2_array.yml b/benchmark/vm2_array.yml
deleted file mode 100644
index 7373098d5e..0000000000
--- a/benchmark/vm2_array.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-benchmark:
- vm2_array: |
- a = [1,2,3,4,5,6,7,8,9,10]
-loop_count: 6000000
diff --git a/benchmark/vm2_bigarray.yml b/benchmark/vm2_bigarray.yml
deleted file mode 100644
index 2ad6da3905..0000000000
--- a/benchmark/vm2_bigarray.yml
+++ /dev/null
@@ -1,105 +0,0 @@
-benchmark:
- vm2_bigarray: |
- a = [
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- 1,2,3,4,5,6,7,8,9,10,
- ]
-loop_count: 6000000
diff --git a/benchmark/vm2_bighash.yml b/benchmark/vm2_bighash.yml
deleted file mode 100644
index e9154e4ba9..0000000000
--- a/benchmark/vm2_bighash.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-benchmark:
- vm2_bighash: |
- a = {0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, 10=>10, 11=>11, 12=>12, 13=>13, 14=>14, 15=>15, 16=>16, 17=>17, 18=>18, 19=>19, 20=>20, 21=>21, 22=>22, 23=>23, 24=>24, 25=>25, 26=>26, 27=>27, 28=>28, 29=>29, 30=>30, 31=>31, 32=>32, 33=>33, 34=>34, 35=>35, 36=>36, 37=>37, 38=>38, 39=>39, 40=>40, 41=>41, 42=>42, 43=>43, 44=>44, 45=>45, 46=>46, 47=>47, 48=>48, 49=>49, 50=>50, 51=>51, 52=>52, 53=>53, 54=>54, 55=>55, 56=>56, 57=>57, 58=>58, 59=>59, 60=>60, 61=>61, 62=>62, 63=>63, 64=>64, 65=>65, 66=>66, 67=>67, 68=>68, 69=>69, 70=>70, 71=>71, 72=>72, 73=>73, 74=>74, 75=>75, 76=>76, 77=>77, 78=>78, 79=>79, 80=>80, 81=>81, 82=>82, 83=>83, 84=>84, 85=>85, 86=>86, 87=>87, 88=>88, 89=>89, 90=>90, 91=>91, 92=>92, 93=>93, 94=>94, 95=>95, 96=>96, 97=>97, 98=>98, 99=>99, 100=>100, 101=>101, 102=>102, 103=>103, 104=>104, 105=>105, 106=>106, 107=>107, 108=>108, 109=>109, 110=>110, 111=>111, 112=>112, 113=>113, 114=>114, 115=>115, 116=>116, 117=>117, 118=>118, 119=>119, 120=>120, 121=>121, 122=>122, 123=>123, 124=>124, 125=>125, 126=>126, 127=>127, 128=>128, 129=>129, 130=>130, 131=>131, 132=>132, 133=>133, 134=>134, 135=>135, 136=>136, 137=>137, 138=>138, 139=>139, 140=>140, 141=>141, 142=>142, 143=>143, 144=>144, 145=>145, 146=>146, 147=>147, 148=>148, 149=>149, 150=>150, 151=>151, 152=>152, 153=>153, 154=>154, 155=>155, 156=>156, 157=>157, 158=>158, 159=>159, 160=>160, 161=>161, 162=>162, 163=>163, 164=>164, 165=>165, 166=>166, 167=>167, 168=>168, 169=>169, 170=>170, 171=>171, 172=>172, 173=>173, 174=>174, 175=>175, 176=>176, 177=>177, 178=>178, 179=>179, 180=>180, 181=>181, 182=>182, 183=>183, 184=>184, 185=>185, 186=>186, 187=>187, 188=>188, 189=>189, 190=>190, 191=>191, 192=>192, 193=>193, 194=>194, 195=>195, 196=>196, 197=>197, 198=>198, 199=>199, 200=>200, 201=>201, 202=>202, 203=>203, 204=>204, 205=>205, 206=>206, 207=>207, 208=>208, 209=>209, 210=>210, 211=>211, 212=>212, 213=>213, 214=>214, 215=>215, 216=>216, 217=>217, 218=>218, 219=>219, 220=>220, 221=>221, 222=>222, 223=>223, 224=>224, 225=>225, 226=>226, 227=>227, 228=>228, 229=>229, 230=>230, 231=>231, 232=>232, 233=>233, 234=>234, 235=>235, 236=>236, 237=>237, 238=>238, 239=>239, 240=>240, 241=>241, 242=>242, 243=>243, 244=>244, 245=>245, 246=>246, 247=>247, 248=>248, 249=>249, 250=>250, 251=>251, 252=>252, 253=>253, 254=>254, 255=>255, 256=>256, 257=>257, 258=>258, 259=>259, 260=>260, 261=>261, 262=>262, 263=>263, 264=>264, 265=>265, 266=>266, 267=>267, 268=>268, 269=>269, 270=>270, 271=>271, 272=>272, 273=>273, 274=>274, 275=>275, 276=>276, 277=>277, 278=>278, 279=>279, 280=>280, 281=>281, 282=>282, 283=>283, 284=>284, 285=>285, 286=>286, 287=>287, 288=>288, 289=>289, 290=>290, 291=>291, 292=>292, 293=>293, 294=>294, 295=>295, 296=>296, 297=>297, 298=>298, 299=>299, 300=>300, 301=>301, 302=>302, 303=>303, 304=>304, 305=>305, 306=>306, 307=>307, 308=>308, 309=>309, 310=>310, 311=>311, 312=>312, 313=>313, 314=>314, 315=>315, 316=>316, 317=>317, 318=>318, 319=>319, 320=>320, 321=>321, 322=>322, 323=>323, 324=>324, 325=>325, 326=>326, 327=>327, 328=>328, 329=>329, 330=>330, 331=>331, 332=>332, 333=>333, 334=>334, 335=>335, 336=>336, 337=>337, 338=>338, 339=>339, 340=>340, 341=>341, 342=>342, 343=>343, 344=>344, 345=>345, 346=>346, 347=>347, 348=>348, 349=>349, 350=>350, 351=>351, 352=>352, 353=>353, 354=>354, 355=>355, 356=>356, 357=>357, 358=>358, 359=>359, 360=>360, 361=>361, 362=>362, 363=>363, 364=>364, 365=>365, 366=>366, 367=>367, 368=>368, 369=>369, 370=>370, 371=>371, 372=>372, 373=>373, 374=>374, 375=>375, 376=>376, 377=>377, 378=>378, 379=>379, 380=>380, 381=>381, 382=>382, 383=>383, 384=>384, 385=>385, 386=>386, 387=>387, 388=>388, 389=>389, 390=>390, 391=>391, 392=>392, 393=>393, 394=>394, 395=>395, 396=>396, 397=>397, 398=>398, 399=>399, 400=>400, 401=>401, 402=>402, 403=>403, 404=>404, 405=>405, 406=>406, 407=>407, 408=>408, 409=>409, 410=>410, 411=>411, 412=>412, 413=>413, 414=>414, 415=>415, 416=>416, 417=>417, 418=>418, 419=>419, 420=>420, 421=>421, 422=>422, 423=>423, 424=>424, 425=>425, 426=>426, 427=>427, 428=>428, 429=>429, 430=>430, 431=>431, 432=>432, 433=>433, 434=>434, 435=>435, 436=>436, 437=>437, 438=>438, 439=>439, 440=>440, 441=>441, 442=>442, 443=>443, 444=>444, 445=>445, 446=>446, 447=>447, 448=>448, 449=>449, 450=>450, 451=>451, 452=>452, 453=>453, 454=>454, 455=>455, 456=>456, 457=>457, 458=>458, 459=>459, 460=>460, 461=>461, 462=>462, 463=>463, 464=>464, 465=>465, 466=>466, 467=>467, 468=>468, 469=>469, 470=>470, 471=>471, 472=>472, 473=>473, 474=>474, 475=>475, 476=>476, 477=>477, 478=>478, 479=>479, 480=>480, 481=>481, 482=>482, 483=>483, 484=>484, 485=>485, 486=>486, 487=>487, 488=>488, 489=>489, 490=>490, 491=>491, 492=>492, 493=>493, 494=>494, 495=>495, 496=>496, 497=>497, 498=>498, 499=>499, 500=>500,}
-loop_count: 60000
diff --git a/benchmark/vm2_case.yml b/benchmark/vm2_case.yml
deleted file mode 100644
index 7716783c09..0000000000
--- a/benchmark/vm2_case.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-benchmark:
- vm2_case: |
- case :foo
- when :bar
- raise
- when :baz
- raise
- when :boo
- raise
- when :foo
- # noop
- end
-loop_count: 6000000
diff --git a/benchmark/vm2_case_lit.yml b/benchmark/vm2_case_lit.yml
deleted file mode 100644
index c49b8dfe5e..0000000000
--- a/benchmark/vm2_case_lit.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-# loop_count is not utilized since `i` is involved in the script
-benchmark:
- vm2_case_lit: |
- i = 0
- @ret = [ "foo", true, false, :sym, 6, nil, 0.1, 0xffffffffffffffff ]
- def foo(i)
- @ret[i % @ret.size]
- end
-
- while i<6_000_000
- case foo(i)
- when "foo" then :foo
- when true then true
- when false then false
- when :sym then :sym
- when 6 then :fix
- when nil then nil
- when 0.1 then :float
- when 0xffffffffffffffff then :big
- end
- i += 1
- end
-loop_count: 1
diff --git a/benchmark/vm2_defined_method.yml b/benchmark/vm2_defined_method.yml
deleted file mode 100644
index e1b0d55674..0000000000
--- a/benchmark/vm2_defined_method.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- class Object
- define_method(:m){}
- end
-benchmark:
- vm2_defined_method: |
- m; m; m; m; m; m; m; m;
-loop_count: 6000000
diff --git a/benchmark/vm2_dstr.yml b/benchmark/vm2_dstr.yml
deleted file mode 100644
index f8bd6e0133..0000000000
--- a/benchmark/vm2_dstr.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-prelude: |
- x = y = 'z'
-benchmark:
- vm2_dstr: |
- str = "foo#{x}bar#{y}baz"
-loop_count: 6000000
diff --git a/benchmark/vm2_eval.yml b/benchmark/vm2_eval.yml
deleted file mode 100644
index d506a9c079..0000000000
--- a/benchmark/vm2_eval.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-benchmark:
- vm2_eval: |
- eval("1")
-loop_count: 6000000
diff --git a/benchmark/vm2_fiber_switch.yml b/benchmark/vm2_fiber_switch.yml
deleted file mode 100644
index f3e4c91283..0000000000
--- a/benchmark/vm2_fiber_switch.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-prelude: |
- # based on benchmark for [ruby-core:65518] [Feature #10341] by Knut Franke
- fib = Fiber.new do
- loop { Fiber.yield }
- end
-benchmark:
- vm2_fiber_switch: |
- fib.resume
-loop_count: 6000000
diff --git a/benchmark/vm2_freezestring.yml b/benchmark/vm2_freezestring.yml
deleted file mode 100644
index b78af91a20..0000000000
--- a/benchmark/vm2_freezestring.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-prelude: |
- class String
- def freeze
- -self
- end
- end
-benchmark:
- vm2_freezestring: |
- "tXnL1BP5T1WPXMjuFNLQtallEtRcay1t2lHtJSrlVsDgvunlbtfpr/DGdH0NGYE9".freeze
-loop_count: 6000000
diff --git a/benchmark/vm2_method.yml b/benchmark/vm2_method.yml
deleted file mode 100644
index cc7b9b28ff..0000000000
--- a/benchmark/vm2_method.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- def m
- nil
- end
-benchmark:
- vm2_method: |
- m; m; m; m; m; m; m; m;
-loop_count: 6000000
diff --git a/benchmark/vm2_method_missing.yml b/benchmark/vm2_method_missing.yml
deleted file mode 100644
index cbfb794b25..0000000000
--- a/benchmark/vm2_method_missing.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-prelude: |
- class C
- def method_missing mid
- end
- end
-
- obj = C.new
-benchmark:
- vm2_method_missing: |
- obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m;
-loop_count: 6000000
diff --git a/benchmark/vm2_method_with_block.yml b/benchmark/vm2_method_with_block.yml
deleted file mode 100644
index 6e522adccc..0000000000
--- a/benchmark/vm2_method_with_block.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- def m
- nil
- end
-benchmark:
- vm2_method_with_block: |
- m{}; m{}; m{}; m{}; m{}; m{}; m{}; m{};
-loop_count: 6000000
diff --git a/benchmark/vm2_module_ann_const_set.yml b/benchmark/vm2_module_ann_const_set.yml
deleted file mode 100644
index b0becd9d3d..0000000000
--- a/benchmark/vm2_module_ann_const_set.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-benchmark:
- vm2_module_ann_const_set: |
- Module.new.const_set(:X, Module.new)
-loop_count: 6000000
diff --git a/benchmark/vm2_module_const_set.yml b/benchmark/vm2_module_const_set.yml
deleted file mode 100644
index 05a640069c..0000000000
--- a/benchmark/vm2_module_const_set.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- module M
- end
- $VERBOSE = nil
-benchmark:
- vm2_module_const_set: |
- M.const_set(:X, Module.new)
-loop_count: 6000000
diff --git a/benchmark/vm2_mutex.yml b/benchmark/vm2_mutex.yml
deleted file mode 100644
index c40a90444a..0000000000
--- a/benchmark/vm2_mutex.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-prelude: |
- require 'thread'
-
- m = Thread::Mutex.new
-benchmark:
- vm2_mutex: |
- m.synchronize{}
-loop_count: 6000000
diff --git a/benchmark/vm2_newlambda.yml b/benchmark/vm2_newlambda.yml
deleted file mode 100644
index 93133f9f30..0000000000
--- a/benchmark/vm2_newlambda.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-benchmark:
- vm2_newlambda: |
- lambda {}
-loop_count: 6000000
diff --git a/benchmark/vm2_poly_method.yml b/benchmark/vm2_poly_method.yml
deleted file mode 100644
index 0104bdfb66..0000000000
--- a/benchmark/vm2_poly_method.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-# loop_count is not utilized since `i` is involved in the script
-benchmark:
- vm2_poly_method: |
- class C1
- def m
- 1
- end
- end
- class C2
- def m
- 2
- end
- end
-
- o1 = C1.new
- o2 = C2.new
-
- i = 0
- while i<6_000_000
- o = (i % 2 == 0) ? o1 : o2
- o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
- i += 1
- end
-loop_count: 1
diff --git a/benchmark/vm2_poly_method_ov.yml b/benchmark/vm2_poly_method_ov.yml
deleted file mode 100644
index 3748073ba2..0000000000
--- a/benchmark/vm2_poly_method_ov.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-# loop_count is not utilized since `i` is involved in the script
-benchmark:
- vm2_poly_method_ov: |
- class C1
- def m
- 1
- end
- end
- class C2
- def m
- 2
- end
- end
-
- o1 = C1.new
- o2 = C2.new
-
- i = 0
- while i<6_000_000
- o = (i % 2 == 0) ? o1 : o2
- # o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
- i += 1
- end
-loop_count: 1
diff --git a/benchmark/vm2_poly_singleton.yml b/benchmark/vm2_poly_singleton.yml
deleted file mode 100644
index e58d7bfb37..0000000000
--- a/benchmark/vm2_poly_singleton.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-# loop_count is not utilized since `i` is involved in the script
-benchmark:
- vm2_poly_singleton: |
- class C1
- def m; 1; end
- end
-
- o1 = C1.new
- o2 = C1.new
- o2.singleton_class
-
- i = 0
- while i<6_000_000 # benchmark loop 2
- o = (i % 2 == 0) ? o1 : o2
- o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
- i += 1
- end
-loop_count: 1
diff --git a/benchmark/vm2_proc.yml b/benchmark/vm2_proc.yml
deleted file mode 100644
index 5c36e936d9..0000000000
--- a/benchmark/vm2_proc.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-prelude: |
- def m &b
- b
- end
-
- pr = m{
- a = 1
- }
-benchmark:
- vm2_proc: |
- pr.call
-loop_count: 6000000
diff --git a/benchmark/vm2_raise1.yml b/benchmark/vm2_raise1.yml
deleted file mode 100644
index f6eb308968..0000000000
--- a/benchmark/vm2_raise1.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-prelude: |
- def rec n
- if n > 0
- rec n-1
- else
- raise
- end
- end
-benchmark:
- vm2_raise1: |
- begin
- rec 1
- rescue
- # ignore
- end
-loop_count: 6000000
diff --git a/benchmark/vm2_raise2.yml b/benchmark/vm2_raise2.yml
deleted file mode 100644
index 7d51b1b314..0000000000
--- a/benchmark/vm2_raise2.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-prelude: |
- def rec n
- if n > 0
- rec n-1
- else
- raise
- end
- end
-benchmark:
- vm2_raise2: |
- begin
- rec 10
- rescue
- # ignore
- end
-loop_count: 6000000
diff --git a/benchmark/vm2_regexp.yml b/benchmark/vm2_regexp.yml
deleted file mode 100644
index 0f3968a99b..0000000000
--- a/benchmark/vm2_regexp.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-prelude: |
- str = 'xxxhogexxx'
-benchmark:
- vm2_regexp: |
- /hoge/ =~ str
-loop_count: 6000000
diff --git a/benchmark/vm2_send.yml b/benchmark/vm2_send.yml
deleted file mode 100644
index 44a12a27d9..0000000000
--- a/benchmark/vm2_send.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-prelude: |
- class C
- def m
- end
- end
-
- o = C.new
-benchmark:
- vm2_send: |
- o.__send__ :m
-loop_count: 6000000
diff --git a/benchmark/vm2_string_literal.yml b/benchmark/vm2_string_literal.yml
deleted file mode 100644
index 54b0aec1fe..0000000000
--- a/benchmark/vm2_string_literal.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-benchmark:
- vm2_string_literal: |
- x = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
-loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_aref_hi.yml b/benchmark/vm2_struct_big_aref_hi.yml
deleted file mode 100644
index eed1846d28..0000000000
--- a/benchmark/vm2_struct_big_aref_hi.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- s = Struct.new(*('a'..'z').map { |x| x.to_sym })
- x = s.new
-benchmark:
- vm2_struct_big_aref_hi: |
- x.z # x[25]
-loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_aref_lo.yml b/benchmark/vm2_struct_big_aref_lo.yml
deleted file mode 100644
index 0915435b76..0000000000
--- a/benchmark/vm2_struct_big_aref_lo.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- s = Struct.new(*('a'..'z').map { |x| x.to_sym })
- x = s.new
-benchmark:
- vm2_struct_big_aref_lo: |
- x.k # x[10]
-loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_aset.yml b/benchmark/vm2_struct_big_aset.yml
deleted file mode 100644
index 6af50103d3..0000000000
--- a/benchmark/vm2_struct_big_aset.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-# loop_count is not utilized since `i` is involved in the script
-benchmark:
- vm2_struct_big_aset: |
- s = Struct.new(*('a'..'z').map { |x| x.to_sym })
- x = s.new
- i = 0
- while i<6_000_000
- i += 1
- x.k = i # x[10] = i
- end
-loop_count: 1
diff --git a/benchmark/vm2_struct_big_href_hi.yml b/benchmark/vm2_struct_big_href_hi.yml
deleted file mode 100644
index 60aa7fddf3..0000000000
--- a/benchmark/vm2_struct_big_href_hi.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- s = Struct.new(*('a'..'z').map { |x| x.to_sym })
- x = s.new
-benchmark:
- vm2_struct_big_href_hi: |
- x[:z]
-loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_href_lo.yml b/benchmark/vm2_struct_big_href_lo.yml
deleted file mode 100644
index c55c0bd16c..0000000000
--- a/benchmark/vm2_struct_big_href_lo.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- s = Struct.new(*('a'..'z').map { |x| x.to_sym })
- x = s.new
-benchmark:
- vm2_struct_big_href_lo: |
- x[:k]
-loop_count: 6000000
diff --git a/benchmark/vm2_struct_big_hset.yml b/benchmark/vm2_struct_big_hset.yml
deleted file mode 100644
index d199c5bd47..0000000000
--- a/benchmark/vm2_struct_big_hset.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-# loop_count is not utilized since `i` is involved in the script
-benchmark:
- vm2_struct_big_hset: |
- s = Struct.new(*('a'..'z').map { |x| x.to_sym })
- x = s.new
- i = 0
- while i<6_000_000
- i += 1
- x[:k] = i
- end
-loop_count: 1
diff --git a/benchmark/vm2_struct_small_aref.yml b/benchmark/vm2_struct_small_aref.yml
deleted file mode 100644
index 83381bed3a..0000000000
--- a/benchmark/vm2_struct_small_aref.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- s = Struct.new(:a, :b, :c)
- x = s.new
-benchmark:
- vm2_struct_small_aref: |
- x.a
-loop_count: 6000000
diff --git a/benchmark/vm2_struct_small_aset.yml b/benchmark/vm2_struct_small_aset.yml
deleted file mode 100644
index 3e84a61dd0..0000000000
--- a/benchmark/vm2_struct_small_aset.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-# loop_count is not utilized since `i` is involved in the script
-benchmark:
- vm2_struct_small_aset: |
- s = Struct.new(:a, :b, :c)
- x = s.new
- i = 0
- while i<6_000_000
- i += 1
- x.a = i
- end
-loop_count: 1
diff --git a/benchmark/vm2_struct_small_href.yml b/benchmark/vm2_struct_small_href.yml
deleted file mode 100644
index b744f070d1..0000000000
--- a/benchmark/vm2_struct_small_href.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- s = Struct.new(:a, :b, :c)
- x = s.new
-benchmark:
- vm2_struct_small_href: |
- x[:a]
-loop_count: 6000000
diff --git a/benchmark/vm2_struct_small_hset.yml b/benchmark/vm2_struct_small_hset.yml
deleted file mode 100644
index d43845d6e0..0000000000
--- a/benchmark/vm2_struct_small_hset.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- s = Struct.new(:a, :b, :c)
- x = s.new
-benchmark:
- vm2_struct_small_hset: |
- x[:a] = 1
-loop_count: 6000000
diff --git a/benchmark/vm2_super.yml b/benchmark/vm2_super.yml
deleted file mode 100644
index 674743762a..0000000000
--- a/benchmark/vm2_super.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-prelude: |
- class C
- def m
- 1
- end
- end
-
- class CC < C
- def m
- super()
- end
- end
-
- obj = CC.new
-benchmark:
- vm2_super: obj.m
-loop_count: 6000000
diff --git a/benchmark/vm2_unif1.yml b/benchmark/vm2_unif1.yml
deleted file mode 100644
index caef13279f..0000000000
--- a/benchmark/vm2_unif1.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-prelude: |
- def m a, b
- end
-benchmark:
- vm2_unif1: |
- m 100, 200
-loop_count: 6000000
diff --git a/benchmark/vm2_zsuper.yml b/benchmark/vm2_zsuper.yml
deleted file mode 100644
index f760cfd48e..0000000000
--- a/benchmark/vm2_zsuper.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-prelude: |
- class C
- def m a
- 1
- end
- end
-
- class CC < C
- def m a
- super
- end
- end
-
- obj = CC.new
-benchmark:
- vm2_zsuper: |
- obj.m 10
-loop_count: 6000000
diff --git a/benchmark/vm_array.yml b/benchmark/vm_array.yml
new file mode 100644
index 0000000000..2a177237ef
--- /dev/null
+++ b/benchmark/vm_array.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm_array: |
+ a = [1,2,3,4,5,6,7,8,9,10]
+loop_count: 6000000
diff --git a/benchmark/vm_attr_ivar.yml b/benchmark/vm_attr_ivar.yml
new file mode 100644
index 0000000000..75b803478e
--- /dev/null
+++ b/benchmark/vm_attr_ivar.yml
@@ -0,0 +1,14 @@
+prelude: |
+ class C
+ attr_reader :a, :b
+ def initialize
+ @a = nil
+ @b = nil
+ end
+ end
+ obj = C.new
+benchmark:
+ vm_attr_ivar: |
+ j = obj.a
+ k = obj.b
+loop_count: 30000000
diff --git a/benchmark/vm_attr_ivar_set.yml b/benchmark/vm_attr_ivar_set.yml
new file mode 100644
index 0000000000..a0d379b18a
--- /dev/null
+++ b/benchmark/vm_attr_ivar_set.yml
@@ -0,0 +1,14 @@
+prelude: |
+ class C
+ attr_accessor :a, :b
+ def initialize
+ @a = nil
+ @b = nil
+ end
+ end
+ obj = C.new
+benchmark:
+ vm_attr_ivar_set: |
+ obj.a = 1
+ obj.b = 2
+loop_count: 30000000
diff --git a/benchmark/vm3_backtrace.rb b/benchmark/vm_backtrace.rb
index 0fbf73e1ca..0fbf73e1ca 100644
--- a/benchmark/vm3_backtrace.rb
+++ b/benchmark/vm_backtrace.rb
diff --git a/benchmark/vm_bigarray.yml b/benchmark/vm_bigarray.yml
new file mode 100644
index 0000000000..8b2d3f3443
--- /dev/null
+++ b/benchmark/vm_bigarray.yml
@@ -0,0 +1,105 @@
+benchmark:
+ vm_bigarray: |
+ a = [
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ 1,2,3,4,5,6,7,8,9,10,
+ ]
+loop_count: 6000000
diff --git a/benchmark/vm_bighash.yml b/benchmark/vm_bighash.yml
new file mode 100644
index 0000000000..4dacfde793
--- /dev/null
+++ b/benchmark/vm_bighash.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm_bighash: |
+ a = {0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, 10=>10, 11=>11, 12=>12, 13=>13, 14=>14, 15=>15, 16=>16, 17=>17, 18=>18, 19=>19, 20=>20, 21=>21, 22=>22, 23=>23, 24=>24, 25=>25, 26=>26, 27=>27, 28=>28, 29=>29, 30=>30, 31=>31, 32=>32, 33=>33, 34=>34, 35=>35, 36=>36, 37=>37, 38=>38, 39=>39, 40=>40, 41=>41, 42=>42, 43=>43, 44=>44, 45=>45, 46=>46, 47=>47, 48=>48, 49=>49, 50=>50, 51=>51, 52=>52, 53=>53, 54=>54, 55=>55, 56=>56, 57=>57, 58=>58, 59=>59, 60=>60, 61=>61, 62=>62, 63=>63, 64=>64, 65=>65, 66=>66, 67=>67, 68=>68, 69=>69, 70=>70, 71=>71, 72=>72, 73=>73, 74=>74, 75=>75, 76=>76, 77=>77, 78=>78, 79=>79, 80=>80, 81=>81, 82=>82, 83=>83, 84=>84, 85=>85, 86=>86, 87=>87, 88=>88, 89=>89, 90=>90, 91=>91, 92=>92, 93=>93, 94=>94, 95=>95, 96=>96, 97=>97, 98=>98, 99=>99, 100=>100, 101=>101, 102=>102, 103=>103, 104=>104, 105=>105, 106=>106, 107=>107, 108=>108, 109=>109, 110=>110, 111=>111, 112=>112, 113=>113, 114=>114, 115=>115, 116=>116, 117=>117, 118=>118, 119=>119, 120=>120, 121=>121, 122=>122, 123=>123, 124=>124, 125=>125, 126=>126, 127=>127, 128=>128, 129=>129, 130=>130, 131=>131, 132=>132, 133=>133, 134=>134, 135=>135, 136=>136, 137=>137, 138=>138, 139=>139, 140=>140, 141=>141, 142=>142, 143=>143, 144=>144, 145=>145, 146=>146, 147=>147, 148=>148, 149=>149, 150=>150, 151=>151, 152=>152, 153=>153, 154=>154, 155=>155, 156=>156, 157=>157, 158=>158, 159=>159, 160=>160, 161=>161, 162=>162, 163=>163, 164=>164, 165=>165, 166=>166, 167=>167, 168=>168, 169=>169, 170=>170, 171=>171, 172=>172, 173=>173, 174=>174, 175=>175, 176=>176, 177=>177, 178=>178, 179=>179, 180=>180, 181=>181, 182=>182, 183=>183, 184=>184, 185=>185, 186=>186, 187=>187, 188=>188, 189=>189, 190=>190, 191=>191, 192=>192, 193=>193, 194=>194, 195=>195, 196=>196, 197=>197, 198=>198, 199=>199, 200=>200, 201=>201, 202=>202, 203=>203, 204=>204, 205=>205, 206=>206, 207=>207, 208=>208, 209=>209, 210=>210, 211=>211, 212=>212, 213=>213, 214=>214, 215=>215, 216=>216, 217=>217, 218=>218, 219=>219, 220=>220, 221=>221, 222=>222, 223=>223, 224=>224, 225=>225, 226=>226, 227=>227, 228=>228, 229=>229, 230=>230, 231=>231, 232=>232, 233=>233, 234=>234, 235=>235, 236=>236, 237=>237, 238=>238, 239=>239, 240=>240, 241=>241, 242=>242, 243=>243, 244=>244, 245=>245, 246=>246, 247=>247, 248=>248, 249=>249, 250=>250, 251=>251, 252=>252, 253=>253, 254=>254, 255=>255, 256=>256, 257=>257, 258=>258, 259=>259, 260=>260, 261=>261, 262=>262, 263=>263, 264=>264, 265=>265, 266=>266, 267=>267, 268=>268, 269=>269, 270=>270, 271=>271, 272=>272, 273=>273, 274=>274, 275=>275, 276=>276, 277=>277, 278=>278, 279=>279, 280=>280, 281=>281, 282=>282, 283=>283, 284=>284, 285=>285, 286=>286, 287=>287, 288=>288, 289=>289, 290=>290, 291=>291, 292=>292, 293=>293, 294=>294, 295=>295, 296=>296, 297=>297, 298=>298, 299=>299, 300=>300, 301=>301, 302=>302, 303=>303, 304=>304, 305=>305, 306=>306, 307=>307, 308=>308, 309=>309, 310=>310, 311=>311, 312=>312, 313=>313, 314=>314, 315=>315, 316=>316, 317=>317, 318=>318, 319=>319, 320=>320, 321=>321, 322=>322, 323=>323, 324=>324, 325=>325, 326=>326, 327=>327, 328=>328, 329=>329, 330=>330, 331=>331, 332=>332, 333=>333, 334=>334, 335=>335, 336=>336, 337=>337, 338=>338, 339=>339, 340=>340, 341=>341, 342=>342, 343=>343, 344=>344, 345=>345, 346=>346, 347=>347, 348=>348, 349=>349, 350=>350, 351=>351, 352=>352, 353=>353, 354=>354, 355=>355, 356=>356, 357=>357, 358=>358, 359=>359, 360=>360, 361=>361, 362=>362, 363=>363, 364=>364, 365=>365, 366=>366, 367=>367, 368=>368, 369=>369, 370=>370, 371=>371, 372=>372, 373=>373, 374=>374, 375=>375, 376=>376, 377=>377, 378=>378, 379=>379, 380=>380, 381=>381, 382=>382, 383=>383, 384=>384, 385=>385, 386=>386, 387=>387, 388=>388, 389=>389, 390=>390, 391=>391, 392=>392, 393=>393, 394=>394, 395=>395, 396=>396, 397=>397, 398=>398, 399=>399, 400=>400, 401=>401, 402=>402, 403=>403, 404=>404, 405=>405, 406=>406, 407=>407, 408=>408, 409=>409, 410=>410, 411=>411, 412=>412, 413=>413, 414=>414, 415=>415, 416=>416, 417=>417, 418=>418, 419=>419, 420=>420, 421=>421, 422=>422, 423=>423, 424=>424, 425=>425, 426=>426, 427=>427, 428=>428, 429=>429, 430=>430, 431=>431, 432=>432, 433=>433, 434=>434, 435=>435, 436=>436, 437=>437, 438=>438, 439=>439, 440=>440, 441=>441, 442=>442, 443=>443, 444=>444, 445=>445, 446=>446, 447=>447, 448=>448, 449=>449, 450=>450, 451=>451, 452=>452, 453=>453, 454=>454, 455=>455, 456=>456, 457=>457, 458=>458, 459=>459, 460=>460, 461=>461, 462=>462, 463=>463, 464=>464, 465=>465, 466=>466, 467=>467, 468=>468, 469=>469, 470=>470, 471=>471, 472=>472, 473=>473, 474=>474, 475=>475, 476=>476, 477=>477, 478=>478, 479=>479, 480=>480, 481=>481, 482=>482, 483=>483, 484=>484, 485=>485, 486=>486, 487=>487, 488=>488, 489=>489, 490=>490, 491=>491, 492=>492, 493=>493, 494=>494, 495=>495, 496=>496, 497=>497, 498=>498, 499=>499, 500=>500,}
+loop_count: 60000
diff --git a/benchmark/vm_block.yml b/benchmark/vm_block.yml
new file mode 100644
index 0000000000..68b3e40bf5
--- /dev/null
+++ b/benchmark/vm_block.yml
@@ -0,0 +1,9 @@
+prelude: |
+ def m
+ yield
+ end
+benchmark:
+ vm_block: |
+ m{
+ }
+loop_count: 30000000
diff --git a/benchmark/vm_block_handler.yml b/benchmark/vm_block_handler.yml
new file mode 100644
index 0000000000..461d7953ad
--- /dev/null
+++ b/benchmark/vm_block_handler.yml
@@ -0,0 +1,27 @@
+# :FIXME: is there a way to benchmark block_handler_type_ifunc?
+
+prelude: |
+ p = proc{_1}
+ o = Object.new
+ def o.each
+ i = 0
+ while i < 3_000_000 do
+ yield i
+ i += 1
+ end
+ end
+
+benchmark:
+ - name: block_handler_type_iseq
+ script: |
+ o.each{_1}
+
+ - name: block_handler_type_symbol
+ script: |
+ o.each(&:itself)
+
+ - name: block_handler_type_proc
+ script: |
+ o.each(&p)
+
+loop_count: 1
diff --git a/benchmark/vm_blockparam.yml b/benchmark/vm_blockparam.yml
new file mode 100644
index 0000000000..5e5a0170a2
--- /dev/null
+++ b/benchmark/vm_blockparam.yml
@@ -0,0 +1,7 @@
+prelude: |
+ def m &b
+ end
+benchmark:
+ vm_blockparam: |
+ m{}
+loop_count: 30000000
diff --git a/benchmark/vm_blockparam_call.yml b/benchmark/vm_blockparam_call.yml
new file mode 100644
index 0000000000..a7d8d366ea
--- /dev/null
+++ b/benchmark/vm_blockparam_call.yml
@@ -0,0 +1,8 @@
+prelude: |
+ def m &b
+ b.call
+ end
+benchmark:
+ vm_blockparam_call: |
+ m{}
+loop_count: 30000000
diff --git a/benchmark/vm_blockparam_pass.yml b/benchmark/vm_blockparam_pass.yml
new file mode 100644
index 0000000000..841f5e7a63
--- /dev/null
+++ b/benchmark/vm_blockparam_pass.yml
@@ -0,0 +1,12 @@
+prelude: |
+ def bp_yield
+ yield
+ end
+
+ def bp_pass &b
+ bp_yield &b
+ end
+benchmark:
+ vm_blockparam_pass: |
+ bp_pass{}
+loop_count: 30000000
diff --git a/benchmark/vm_blockparam_yield.yml b/benchmark/vm_blockparam_yield.yml
new file mode 100644
index 0000000000..8ea9b46ed2
--- /dev/null
+++ b/benchmark/vm_blockparam_yield.yml
@@ -0,0 +1,8 @@
+prelude: |
+ def bp_yield &b
+ yield
+ end
+benchmark:
+ vm_blockparam_yield: |
+ bp_yield{}
+loop_count: 30000000
diff --git a/benchmark/vm_case.yml b/benchmark/vm_case.yml
new file mode 100644
index 0000000000..b26a491a15
--- /dev/null
+++ b/benchmark/vm_case.yml
@@ -0,0 +1,13 @@
+benchmark:
+ vm_case: |
+ case :foo
+ when :bar
+ raise
+ when :baz
+ raise
+ when :boo
+ raise
+ when :foo
+ # noop
+ end
+loop_count: 6000000
diff --git a/benchmark/vm_case_classes.yml b/benchmark/vm_case_classes.yml
new file mode 100644
index 0000000000..cacc4f0464
--- /dev/null
+++ b/benchmark/vm_case_classes.yml
@@ -0,0 +1,9 @@
+benchmark:
+ vm_case_classes: |
+ case :foo
+ when Hash
+ raise
+ when Array
+ raise
+ end
+loop_count: 6000000
diff --git a/benchmark/vm_case_lit.yml b/benchmark/vm_case_lit.yml
new file mode 100644
index 0000000000..9f91801544
--- /dev/null
+++ b/benchmark/vm_case_lit.yml
@@ -0,0 +1,23 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm_case_lit: |
+ i = 0
+ @ret = [ "foo", true, false, :sym, 6, nil, 0.1, 0xffffffffffffffff ]
+ def foo(i)
+ @ret[i % @ret.size]
+ end
+
+ while i<6_000_000
+ case foo(i)
+ when "foo" then :foo
+ when true then true
+ when false then false
+ when :sym then :sym
+ when 6 then :fix
+ when nil then nil
+ when 0.1 then :float
+ when 0xffffffffffffffff then :big
+ end
+ i += 1
+ end
+loop_count: 1
diff --git a/benchmark/vm3_clearmethodcache.rb b/benchmark/vm_clearmethodcache.rb
index 9661323cd2..9661323cd2 100644
--- a/benchmark/vm3_clearmethodcache.rb
+++ b/benchmark/vm_clearmethodcache.rb
diff --git a/benchmark/vm_const.yml b/benchmark/vm_const.yml
new file mode 100644
index 0000000000..8939ca0cd3
--- /dev/null
+++ b/benchmark/vm_const.yml
@@ -0,0 +1,13 @@
+prelude: |
+ Const = 1
+ A = B = C = D = E = F = G = H = I = J = K = L = M = N = O = P = Q = R = S = T = U = V = W = X = Y = Z = 1
+ def foo
+ A; B; C; D; E; F; G; H; I; J; K; L; M; N; O; P; Q; R; S; T; U; V; W; X; Y; Z
+ end
+benchmark:
+ vm_const: |
+ j = Const
+ k = Const
+ vm_const_many: |
+ foo
+loop_count: 30000000
diff --git a/benchmark/vm_cvar.yml b/benchmark/vm_cvar.yml
new file mode 100644
index 0000000000..1d0e161829
--- /dev/null
+++ b/benchmark/vm_cvar.yml
@@ -0,0 +1,20 @@
+prelude: |
+ class A
+ @@foo = 1
+
+ def self.foo
+ @@foo
+ end
+
+ ("A".."Z").each do |module_name|
+ eval <<-EOM
+ module #{module_name}
+ end
+
+ include #{module_name}
+ EOM
+ end
+ end
+benchmark:
+ vm_cvar: A.foo
+loop_count: 600000
diff --git a/benchmark/vm_defined_method.yml b/benchmark/vm_defined_method.yml
new file mode 100644
index 0000000000..347e0cfd33
--- /dev/null
+++ b/benchmark/vm_defined_method.yml
@@ -0,0 +1,8 @@
+prelude: |
+ class Object
+ define_method(:m){}
+ end
+benchmark:
+ vm_defined_method: |
+ m; m; m; m; m; m; m; m;
+loop_count: 6000000
diff --git a/benchmark/vm_dstr.yml b/benchmark/vm_dstr.yml
new file mode 100644
index 0000000000..30c7a3193c
--- /dev/null
+++ b/benchmark/vm_dstr.yml
@@ -0,0 +1,6 @@
+prelude: |
+ x = y = 'z'
+benchmark:
+ vm_dstr: |
+ str = "foo#{x}bar#{y}baz"
+loop_count: 6000000
diff --git a/benchmark/vm_dstr_ary.rb b/benchmark/vm_dstr_ary.rb
new file mode 100644
index 0000000000..1d3aa3b97b
--- /dev/null
+++ b/benchmark/vm_dstr_ary.rb
@@ -0,0 +1,6 @@
+i = 0
+x = y = []
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
diff --git a/benchmark/vm_dstr_bool.rb b/benchmark/vm_dstr_bool.rb
new file mode 100644
index 0000000000..631ca54755
--- /dev/null
+++ b/benchmark/vm_dstr_bool.rb
@@ -0,0 +1,7 @@
+i = 0
+x = true
+y = false
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
diff --git a/benchmark/vm_dstr_class_module.rb b/benchmark/vm_dstr_class_module.rb
new file mode 100644
index 0000000000..becf0861c7
--- /dev/null
+++ b/benchmark/vm_dstr_class_module.rb
@@ -0,0 +1,10 @@
+i = 0
+class A; end unless defined?(A)
+module B; end unless defined?(B)
+x = A
+y = B
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
+
diff --git a/benchmark/vm_dstr_digit.rb b/benchmark/vm_dstr_digit.rb
new file mode 100644
index 0000000000..caaa395192
--- /dev/null
+++ b/benchmark/vm_dstr_digit.rb
@@ -0,0 +1,7 @@
+i = 0
+x = 0
+y = 9
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
diff --git a/benchmark/vm_dstr_int.rb b/benchmark/vm_dstr_int.rb
new file mode 100644
index 0000000000..ed380d7595
--- /dev/null
+++ b/benchmark/vm_dstr_int.rb
@@ -0,0 +1,5 @@
+i = 0
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{i}bar#{i}baz"
+end
diff --git a/benchmark/vm_dstr_nil.rb b/benchmark/vm_dstr_nil.rb
new file mode 100644
index 0000000000..ec4f5d6c67
--- /dev/null
+++ b/benchmark/vm_dstr_nil.rb
@@ -0,0 +1,6 @@
+i = 0
+x = y = nil
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
diff --git a/benchmark/vm_dstr_obj.rb b/benchmark/vm_dstr_obj.rb
new file mode 100644
index 0000000000..fb78637ead
--- /dev/null
+++ b/benchmark/vm_dstr_obj.rb
@@ -0,0 +1,6 @@
+i = 0
+x = y = Object.new
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
diff --git a/benchmark/vm_dstr_obj_def.rb b/benchmark/vm_dstr_obj_def.rb
new file mode 100644
index 0000000000..99ff7b98fb
--- /dev/null
+++ b/benchmark/vm_dstr_obj_def.rb
@@ -0,0 +1,8 @@
+i = 0
+o = Object.new
+def o.to_s; -""; end
+x = y = o
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
diff --git a/benchmark/vm_dstr_str.rb b/benchmark/vm_dstr_str.rb
new file mode 100644
index 0000000000..45fc107892
--- /dev/null
+++ b/benchmark/vm_dstr_str.rb
@@ -0,0 +1,6 @@
+i = 0
+x = y = ""
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
diff --git a/benchmark/vm_dstr_sym.rb b/benchmark/vm_dstr_sym.rb
new file mode 100644
index 0000000000..484b8f8150
--- /dev/null
+++ b/benchmark/vm_dstr_sym.rb
@@ -0,0 +1,6 @@
+i = 0
+x = y = :z
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str = "foo#{x}bar#{y}baz"
+end
diff --git a/benchmark/vm_ensure.yml b/benchmark/vm_ensure.yml
new file mode 100644
index 0000000000..4ea62f30de
--- /dev/null
+++ b/benchmark/vm_ensure.yml
@@ -0,0 +1,14 @@
+# Not utilizing loop_count since using it for this is too unstable for now
+benchmark:
+ vm_ensure: |
+ i = 0
+ while i<30_000_000
+ i += 1
+ begin
+ begin
+ ensure
+ end
+ ensure
+ end
+ end
+loop_count: 1
diff --git a/benchmark/vm_eval.yml b/benchmark/vm_eval.yml
new file mode 100644
index 0000000000..7ba1a8d1de
--- /dev/null
+++ b/benchmark/vm_eval.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm_eval: |
+ eval("1")
+loop_count: 6000000
diff --git a/benchmark/vm_fiber_allocate.yml b/benchmark/vm_fiber_allocate.yml
new file mode 100644
index 0000000000..b5a54e1ddf
--- /dev/null
+++ b/benchmark/vm_fiber_allocate.yml
@@ -0,0 +1,8 @@
+prelude: |
+ # Disable GC to see raw throughput:
+ GC.disable
+benchmark:
+ vm_fiber_allocate: |
+ fiber = Fiber.new{Fiber.yield}
+ fiber.resume
+loop_count: 100000
diff --git a/benchmark/vm_fiber_count.yml b/benchmark/vm_fiber_count.yml
new file mode 100644
index 0000000000..b83d3152d4
--- /dev/null
+++ b/benchmark/vm_fiber_count.yml
@@ -0,0 +1,10 @@
+# On Linux, you will need to increase the maximum number of memory maps:
+# sudo sysctl -w vm.max_map_count=200000
+prelude: |
+ fibers = []
+benchmark:
+ vm_fiber_count: |
+ fiber = Fiber.new{Fiber.yield}
+ fibers << fiber
+ fiber.resume
+loop_count: 100000
diff --git a/benchmark/vm_fiber_reuse.yml b/benchmark/vm_fiber_reuse.yml
new file mode 100644
index 0000000000..4ca41085b1
--- /dev/null
+++ b/benchmark/vm_fiber_reuse.yml
@@ -0,0 +1,14 @@
+prelude: |
+ GC.disable
+ fibers = []
+benchmark:
+ vm_fiber_reuse: |
+ 1024.times do
+ fiber = Fiber.new{Fiber.yield}
+ fibers << fiber
+ fiber.resume
+ end
+
+ fibers.clear
+ GC.start
+loop_count: 200
diff --git a/benchmark/vm_fiber_reuse_gc.yml b/benchmark/vm_fiber_reuse_gc.yml
new file mode 100644
index 0000000000..892622f121
--- /dev/null
+++ b/benchmark/vm_fiber_reuse_gc.yml
@@ -0,0 +1,12 @@
+# https://bugs.ruby-lang.org/issues/16009
+prelude: |
+ fibers = []
+benchmark:
+ vm_fiber_reuse_gc: |
+ 2000.times do
+ fiber = Fiber.new{Fiber.yield}
+ fibers << fiber
+ fiber.resume
+ end
+ fibers.clear
+loop_count: 100
diff --git a/benchmark/vm_fiber_switch.yml b/benchmark/vm_fiber_switch.yml
new file mode 100644
index 0000000000..3de36b66eb
--- /dev/null
+++ b/benchmark/vm_fiber_switch.yml
@@ -0,0 +1,9 @@
+prelude: |
+ # based on benchmark for [ruby-core:65518] [Feature #10341] by Knut Franke
+ fib = Fiber.new do
+ loop { Fiber.yield }
+ end
+benchmark:
+ vm_fiber_switch: |
+ fib.resume
+loop_count: 20000000
diff --git a/benchmark/vm_float_simple.yml b/benchmark/vm_float_simple.yml
new file mode 100644
index 0000000000..92f5fd52ab
--- /dev/null
+++ b/benchmark/vm_float_simple.yml
@@ -0,0 +1,8 @@
+prelude: |
+ f = 0.0
+benchmark:
+ vm_float_simple: |
+ f += 0.1; f -= 0.1
+ f += 0.1; f -= 0.1
+ f += 0.1; f -= 0.1
+loop_count: 30000000
diff --git a/benchmark/vm_freezeobj.yml b/benchmark/vm_freezeobj.yml
new file mode 100644
index 0000000000..69a795a354
--- /dev/null
+++ b/benchmark/vm_freezeobj.yml
@@ -0,0 +1,6 @@
+prelude: |
+ objs = 100000.times.map { Object.new }
+benchmark:
+ vm_freeze_obj: |
+ objs.map(&:freeze)
+loop_count: 600
diff --git a/benchmark/vm_freezestring.yml b/benchmark/vm_freezestring.yml
new file mode 100644
index 0000000000..facc9aa043
--- /dev/null
+++ b/benchmark/vm_freezestring.yml
@@ -0,0 +1,10 @@
+prelude: |
+ class String
+ def freeze
+ -self
+ end
+ end
+benchmark:
+ vm_freezestring: |
+ "tXnL1BP5T1WPXMjuFNLQtallEtRcay1t2lHtJSrlVsDgvunlbtfpr/DGdH0NGYE9".freeze
+loop_count: 6000000
diff --git a/benchmark/vm3_gc.rb b/benchmark/vm_gc.rb
index e668026915..e668026915 100644
--- a/benchmark/vm3_gc.rb
+++ b/benchmark/vm_gc.rb
diff --git a/benchmark/vm3_gc_old_full.rb b/benchmark/vm_gc_old_full.rb
index cfdfc8c5a5..cfdfc8c5a5 100644
--- a/benchmark/vm3_gc_old_full.rb
+++ b/benchmark/vm_gc_old_full.rb
diff --git a/benchmark/vm3_gc_old_immediate.rb b/benchmark/vm_gc_old_immediate.rb
index ad22feb655..ad22feb655 100644
--- a/benchmark/vm3_gc_old_immediate.rb
+++ b/benchmark/vm_gc_old_immediate.rb
diff --git a/benchmark/vm3_gc_old_lazy.rb b/benchmark/vm_gc_old_lazy.rb
index b74d44baf1..b74d44baf1 100644
--- a/benchmark/vm3_gc_old_lazy.rb
+++ b/benchmark/vm_gc_old_lazy.rb
diff --git a/benchmark/vm_gc_short_lived.yml b/benchmark/vm_gc_short_lived.yml
new file mode 100644
index 0000000000..29c803fee3
--- /dev/null
+++ b/benchmark/vm_gc_short_lived.yml
@@ -0,0 +1,9 @@
+benchmark:
+ vm_gc_short_lived: |
+ a = '' # short-lived String
+ b = ''
+ c = ''
+ d = ''
+ e = ''
+ f = ''
+loop_count: 30000000
diff --git a/benchmark/vm_gc_short_with_complex_long.yml b/benchmark/vm_gc_short_with_complex_long.yml
new file mode 100644
index 0000000000..4b6c3ed7b9
--- /dev/null
+++ b/benchmark/vm_gc_short_with_complex_long.yml
@@ -0,0 +1,25 @@
+prelude: |
+ def nested_hash h, n
+ if n == 0
+ ''
+ else
+ 10.times{
+ h[Object.new] = nested_hash(h, n-1)
+ }
+ end
+ end
+
+ long_lived = Hash.new
+ nested_hash long_lived, 6
+
+ GC.start
+ GC.start
+benchmark:
+ vm_gc_short_with_complex_long: |
+ a = '' # short-lived String
+ b = ''
+ c = ''
+ d = ''
+ e = ''
+ f = ''
+loop_count: 30000000
diff --git a/benchmark/vm_gc_short_with_long.yml b/benchmark/vm_gc_short_with_long.yml
new file mode 100644
index 0000000000..03ba0f95a9
--- /dev/null
+++ b/benchmark/vm_gc_short_with_long.yml
@@ -0,0 +1,13 @@
+prelude: |
+ long_lived = Array.new(1_000_000){|i| "#{i}"}
+ GC.start
+ GC.start
+benchmark:
+ vm_gc_short_with_long: |
+ a = '' # short-lived String
+ b = ''
+ c = ''
+ d = ''
+ e = ''
+ f = ''
+loop_count: 30000000
diff --git a/benchmark/vm_gc_short_with_symbol.yml b/benchmark/vm_gc_short_with_symbol.yml
new file mode 100644
index 0000000000..129b8bf4ed
--- /dev/null
+++ b/benchmark/vm_gc_short_with_symbol.yml
@@ -0,0 +1,13 @@
+prelude: |
+ 50_000.times{|i| sym = "sym#{i}".to_sym}
+ GC.start
+ GC.start
+benchmark:
+ vm_gc_short_with_symbol: |
+ a = '' # short-lived String
+ b = ''
+ c = ''
+ d = ''
+ e = ''
+ f = ''
+loop_count: 30000000
diff --git a/benchmark/vm_gc_wb_ary.yml b/benchmark/vm_gc_wb_ary.yml
new file mode 100644
index 0000000000..e3293e72d0
--- /dev/null
+++ b/benchmark/vm_gc_wb_ary.yml
@@ -0,0 +1,12 @@
+prelude: |
+ short_lived_ary = []
+
+ if RUBY_VERSION >= "2.2.0"
+ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true)
+ end
+
+ short_lived = ''
+benchmark:
+ vm_gc_wb_ary: |
+ short_lived_ary[0] = short_lived # write barrier
+loop_count: 30000000
diff --git a/benchmark/vm_gc_wb_ary_promoted.yml b/benchmark/vm_gc_wb_ary_promoted.yml
new file mode 100644
index 0000000000..003995945b
--- /dev/null
+++ b/benchmark/vm_gc_wb_ary_promoted.yml
@@ -0,0 +1,15 @@
+prelude: |
+ long_lived = []
+
+ if RUBY_VERSION > "2.2.0"
+ 3.times{ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) }
+ elsif
+ GC.start
+ end
+
+ short_lived = ''
+
+benchmark:
+ vm_gc_wb_ary_promoted: |
+ long_lived[0] = short_lived # write barrier
+loop_count: 30000000
diff --git a/benchmark/vm_gc_wb_obj.yml b/benchmark/vm_gc_wb_obj.yml
new file mode 100644
index 0000000000..a2a2ce2d18
--- /dev/null
+++ b/benchmark/vm_gc_wb_obj.yml
@@ -0,0 +1,15 @@
+prelude: |
+ class C
+ attr_accessor :foo
+ end
+ short_lived_obj = C.new
+
+ if RUBY_VERSION >= "2.2.0"
+ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true)
+ end
+
+ short_lived = ''
+benchmark:
+ vm_gc_wb_obj: |
+ short_lived_obj.foo = short_lived # write barrier
+loop_count: 30000000
diff --git a/benchmark/vm_gc_wb_obj_promoted.yml b/benchmark/vm_gc_wb_obj_promoted.yml
new file mode 100644
index 0000000000..00a454ba72
--- /dev/null
+++ b/benchmark/vm_gc_wb_obj_promoted.yml
@@ -0,0 +1,17 @@
+prelude: |
+ class C
+ attr_accessor :foo
+ end
+ long_lived = C.new
+
+ if RUBY_VERSION >= "2.2.0"
+ 3.times{ GC.start(full_mark: false, immediate_mark: true, immediate_sweep: true) }
+ elsif
+ GC.start
+ end
+
+ short_lived = ''
+benchmark:
+ vm_gc_wb_obj_promoted: |
+ long_lived.foo = short_lived # write barrier
+loop_count: 30000000
diff --git a/benchmark/vm_iclass_super.yml b/benchmark/vm_iclass_super.yml
new file mode 100644
index 0000000000..21bb7db247
--- /dev/null
+++ b/benchmark/vm_iclass_super.yml
@@ -0,0 +1,20 @@
+prelude: |
+ class C
+ def m
+ 1
+ end
+
+ ("A".."M").each do |module_name|
+ eval <<-EOM
+ module #{module_name}
+ def m; super; end
+ end
+ prepend #{module_name}
+ EOM
+ end
+ end
+
+ obj = C.new
+benchmark:
+ vm_iclass_super: obj.m
+loop_count: 6000000
diff --git a/benchmark/vm_ivar.yml b/benchmark/vm_ivar.yml
new file mode 100644
index 0000000000..119531d5ef
--- /dev/null
+++ b/benchmark/vm_ivar.yml
@@ -0,0 +1,6 @@
+prelude: "@a = 1\n"
+benchmark:
+ vm_ivar: |
+ j = @a
+ k = @a
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_embedded_obj_init.yml b/benchmark/vm_ivar_embedded_obj_init.yml
new file mode 100644
index 0000000000..74fe20a630
--- /dev/null
+++ b/benchmark/vm_ivar_embedded_obj_init.yml
@@ -0,0 +1,14 @@
+prelude: |
+ class C
+ def set_ivars
+ @a = nil
+ @b = nil
+ @c = nil
+ end
+ end
+
+ c = C.new
+benchmark:
+ vm_ivar_embedded_obj_init: |
+ c.set_ivars
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_extended_obj_init.yml b/benchmark/vm_ivar_extended_obj_init.yml
new file mode 100644
index 0000000000..f054bab282
--- /dev/null
+++ b/benchmark/vm_ivar_extended_obj_init.yml
@@ -0,0 +1,16 @@
+prelude: |
+ class C
+ def set_ivars
+ @a = nil
+ @b = nil
+ @c = nil
+ @d = nil
+ @e = nil
+ end
+ end
+
+ c = C.new
+benchmark:
+ vm_ivar_extended_obj_init: |
+ c.set_ivars
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_generic_get.yml b/benchmark/vm_ivar_generic_get.yml
new file mode 100644
index 0000000000..dae2d37671
--- /dev/null
+++ b/benchmark/vm_ivar_generic_get.yml
@@ -0,0 +1,17 @@
+prelude: |
+ class C < Array
+ attr_reader :a, :b, :c
+ def initialize
+ @a = nil
+ @b = nil
+ @c = nil
+ end
+ end
+
+ c = C.new
+benchmark:
+ vm_ivar_generic_get: |
+ c.a
+ c.b
+ c.c
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_generic_set.yml b/benchmark/vm_ivar_generic_set.yml
new file mode 100644
index 0000000000..102a6577fb
--- /dev/null
+++ b/benchmark/vm_ivar_generic_set.yml
@@ -0,0 +1,14 @@
+prelude: |
+ class C < Array
+ def set_ivars
+ @a = nil
+ @b = nil
+ @c = nil
+ end
+ end
+
+ c = C.new
+benchmark:
+ vm_ivar_generic_set: |
+ c.set_ivars
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_get.yml b/benchmark/vm_ivar_get.yml
new file mode 100644
index 0000000000..9174af6965
--- /dev/null
+++ b/benchmark/vm_ivar_get.yml
@@ -0,0 +1,37 @@
+prelude: |
+ class Example
+ def initialize
+ @v0 = 1
+ @v1 = 2
+ @v3 = 3
+ @levar = 1
+ end
+
+ def get_value_loop
+ sum = 0
+
+ i = 0
+ while i < 1000000
+ # 10 times to de-emphasize loop overhead
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ sum += @levar
+ i += 1
+ end
+
+ return sum
+ end
+ end
+
+ obj = Example.new
+benchmark:
+ vm_ivar_get: |
+ obj.get_value_loop
+loop_count: 100
diff --git a/benchmark/vm_ivar_get_unintialized.yml b/benchmark/vm_ivar_get_unintialized.yml
new file mode 100644
index 0000000000..a1ccfb06ce
--- /dev/null
+++ b/benchmark/vm_ivar_get_unintialized.yml
@@ -0,0 +1,12 @@
+prelude: |
+ class Example
+ def read
+ @uninitialized
+ end
+ end
+
+ obj = Example.new
+benchmark:
+ vm_ivar_get_uninitialized: |
+ obj.read
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_lazy_set.yml b/benchmark/vm_ivar_lazy_set.yml
new file mode 100644
index 0000000000..7372ffcfbc
--- /dev/null
+++ b/benchmark/vm_ivar_lazy_set.yml
@@ -0,0 +1,12 @@
+prelude: |
+ class Example
+ def lazy_set
+ @uninitialized ||= 123
+ end
+ end
+
+ objs = 10000000.times.map { Example.new }
+benchmark:
+ vm_ivar_lazy_set: |
+ objs.each(&:lazy_set)
+loop_count: 1
diff --git a/benchmark/vm_ivar_of_class.yml b/benchmark/vm_ivar_of_class.yml
new file mode 100644
index 0000000000..172e28b2fd
--- /dev/null
+++ b/benchmark/vm_ivar_of_class.yml
@@ -0,0 +1,12 @@
+prelude: |
+ class C
+ @a = 1
+ def self.a
+ _a = @a; _a = @a; _a = @a; _a = @a; _a = @a;
+ _a = @a; _a = @a; _a = @a; _a = @a; _a = @a;
+ end
+ end
+benchmark:
+ vm_ivar_of_class: |
+ a = C.a
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_of_class_set.yml b/benchmark/vm_ivar_of_class_set.yml
new file mode 100644
index 0000000000..2ea5199423
--- /dev/null
+++ b/benchmark/vm_ivar_of_class_set.yml
@@ -0,0 +1,11 @@
+prelude: |
+ class C
+ @a = 1
+ def self.a o
+ @a = o; @a = o; @a = o; @a = o; @a = o; @a = o;
+ end
+ end
+benchmark:
+ vm_ivar_of_class_set: |
+ a = C.a(nil)
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_set.yml b/benchmark/vm_ivar_set.yml
new file mode 100644
index 0000000000..8bbb60043b
--- /dev/null
+++ b/benchmark/vm_ivar_set.yml
@@ -0,0 +1,5 @@
+benchmark:
+ vm_ivar_set: |
+ @a = 1
+ @b = 2
+loop_count: 30000000
diff --git a/benchmark/vm_ivar_set_on_instance.yml b/benchmark/vm_ivar_set_on_instance.yml
new file mode 100644
index 0000000000..91857b7742
--- /dev/null
+++ b/benchmark/vm_ivar_set_on_instance.yml
@@ -0,0 +1,35 @@
+prelude: |
+ class TheClass
+ def initialize
+ @v0 = 1
+ @v1 = 2
+ @v3 = 3
+ @levar = 1
+ end
+
+ def set_value_loop
+ # 1M
+ i = 0
+ while i < 1000000
+ # 10 times to de-emphasize loop overhead
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ @levar = i
+ i += 1
+ end
+ end
+ end
+
+ obj = TheClass.new
+
+benchmark:
+ vm_ivar_set_on_instance: |
+ obj.set_value_loop
+loop_count: 100
diff --git a/benchmark/vm_ivar_set_subclass.yml b/benchmark/vm_ivar_set_subclass.yml
new file mode 100644
index 0000000000..bc8bf5bf6b
--- /dev/null
+++ b/benchmark/vm_ivar_set_subclass.yml
@@ -0,0 +1,20 @@
+prelude: |
+ class A
+ def set_ivars
+ @a = nil
+ @b = nil
+ @c = nil
+ @d = nil
+ @e = nil
+ end
+ end
+ class B < A; end
+ class C < A; end
+
+ b = B.new
+ c = C.new
+benchmark:
+ vm_ivar_init_subclass: |
+ b.set_ivars
+ c.set_ivars
+loop_count: 3000000
diff --git a/benchmark/vm_length.yml b/benchmark/vm_length.yml
new file mode 100644
index 0000000000..5fd94e7d86
--- /dev/null
+++ b/benchmark/vm_length.yml
@@ -0,0 +1,8 @@
+prelude: |
+ a = 'abc'
+ b = [1, 2, 3]
+benchmark:
+ vm_length: |
+ a.length
+ b.length
+loop_count: 30000000
diff --git a/benchmark/vm_lvar_cond_set.yml b/benchmark/vm_lvar_cond_set.yml
new file mode 100644
index 0000000000..1845f9d12e
--- /dev/null
+++ b/benchmark/vm_lvar_cond_set.yml
@@ -0,0 +1,8 @@
+benchmark:
+ vm_lvar_cond_set: |
+ a ||= 1
+ b ||= 1
+ c ||= 1
+ d ||= 1
+ nil
+loop_count: 30000000
diff --git a/benchmark/vm_lvar_init.yml b/benchmark/vm_lvar_init.yml
new file mode 100644
index 0000000000..70a9b1c0ca
--- /dev/null
+++ b/benchmark/vm_lvar_init.yml
@@ -0,0 +1,21 @@
+# while loop cost is not removed because `i` is used in the script
+benchmark:
+ vm_lvar_init: |
+ def m v
+ unless v
+ # unreachable code
+ v1 = v2 = v3 = v4 = v5 = v6 = v7 = v8 = v9 = v10 =
+ v11 = v12 = v13 = v14 = v15 = v16 = v17 = v18 = v19 = v20 =
+ v21 = v22 = v23 = v24 = v25 = v26 = v27 = v28 = v29 = v30 =
+ v31 = v32 = v33 = v34 = v35 = v36 = v37 = v38 = v39 = v40 =
+ v41 = v42 = v43 = v44 = v45 = v46 = v47 = v48 = v49 = v50 = 1
+ end
+ end
+
+ i = 0
+
+ while i<30_000_000
+ i += 1
+ m i
+ end
+loop_count: 1
diff --git a/benchmark/vm_lvar_set.yml b/benchmark/vm_lvar_set.yml
new file mode 100644
index 0000000000..f29f763d81
--- /dev/null
+++ b/benchmark/vm_lvar_set.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm_lvar_set: |
+ a = b = c = d = e = f = g = h = j = k = l = m = n = o = p = q = r = 1
+loop_count: 30000000
diff --git a/benchmark/vm_method.yml b/benchmark/vm_method.yml
new file mode 100644
index 0000000000..d45e4ec572
--- /dev/null
+++ b/benchmark/vm_method.yml
@@ -0,0 +1,8 @@
+prelude: |
+ def m
+ nil
+ end
+benchmark:
+ vm_method: |
+ m; m; m; m; m; m; m; m;
+loop_count: 6000000
diff --git a/benchmark/vm_method_missing.yml b/benchmark/vm_method_missing.yml
new file mode 100644
index 0000000000..3da456c0bb
--- /dev/null
+++ b/benchmark/vm_method_missing.yml
@@ -0,0 +1,11 @@
+prelude: |
+ class C
+ def method_missing mid
+ end
+ end
+
+ obj = C.new
+benchmark:
+ vm_method_missing: |
+ obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m; obj.m;
+loop_count: 6000000
diff --git a/benchmark/vm_method_with_block.yml b/benchmark/vm_method_with_block.yml
new file mode 100644
index 0000000000..281a481394
--- /dev/null
+++ b/benchmark/vm_method_with_block.yml
@@ -0,0 +1,8 @@
+prelude: |
+ def m
+ nil
+ end
+benchmark:
+ vm_method_with_block: |
+ m{}; m{}; m{}; m{}; m{}; m{}; m{}; m{};
+loop_count: 6000000
diff --git a/benchmark/vm_module_ann_const_set.yml b/benchmark/vm_module_ann_const_set.yml
new file mode 100644
index 0000000000..243229ba4a
--- /dev/null
+++ b/benchmark/vm_module_ann_const_set.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm_module_ann_const_set: |
+ Module.new.const_set(:X, Module.new)
+loop_count: 6000000
diff --git a/benchmark/vm_module_const_set.yml b/benchmark/vm_module_const_set.yml
new file mode 100644
index 0000000000..e5a24181a9
--- /dev/null
+++ b/benchmark/vm_module_const_set.yml
@@ -0,0 +1,8 @@
+prelude: |
+ module M
+ end
+ $VERBOSE = nil
+benchmark:
+ vm_module_const_set: |
+ M.const_set(:X, Module.new)
+loop_count: 6000000
diff --git a/benchmark/vm_mutex.yml b/benchmark/vm_mutex.yml
new file mode 100644
index 0000000000..abcf1e28ce
--- /dev/null
+++ b/benchmark/vm_mutex.yml
@@ -0,0 +1,8 @@
+prelude: |
+ require 'thread'
+
+ m = Thread::Mutex.new
+benchmark:
+ vm_mutex: |
+ m.synchronize{}
+loop_count: 6000000
diff --git a/benchmark/vm_neq.yml b/benchmark/vm_neq.yml
new file mode 100644
index 0000000000..fb04d15ae8
--- /dev/null
+++ b/benchmark/vm_neq.yml
@@ -0,0 +1,7 @@
+prelude: |
+ obj1 = Object.new
+ obj2 = Object.new
+benchmark:
+ vm_neq: |
+ obj1 != obj2
+loop_count: 30000000
diff --git a/benchmark/vm_newlambda.yml b/benchmark/vm_newlambda.yml
new file mode 100644
index 0000000000..0b9787d91a
--- /dev/null
+++ b/benchmark/vm_newlambda.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm_newlambda: |
+ lambda {}
+loop_count: 6000000
diff --git a/benchmark/vm_not.yml b/benchmark/vm_not.yml
new file mode 100644
index 0000000000..c68dde3c50
--- /dev/null
+++ b/benchmark/vm_not.yml
@@ -0,0 +1,6 @@
+prelude: |
+ obj = Object.new
+benchmark:
+ vm_not: |
+ !obj
+loop_count: 30000000
diff --git a/benchmark/vm_poly_method.yml b/benchmark/vm_poly_method.yml
new file mode 100644
index 0000000000..dd2f4e71de
--- /dev/null
+++ b/benchmark/vm_poly_method.yml
@@ -0,0 +1,24 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm_poly_method: |
+ class C1
+ def m
+ 1
+ end
+ end
+ class C2
+ def m
+ 2
+ end
+ end
+
+ o1 = C1.new
+ o2 = C2.new
+
+ i = 0
+ while i<6_000_000
+ o = (i % 2 == 0) ? o1 : o2
+ o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
+ i += 1
+ end
+loop_count: 1
diff --git a/benchmark/vm_poly_method_ov.yml b/benchmark/vm_poly_method_ov.yml
new file mode 100644
index 0000000000..bca1b62729
--- /dev/null
+++ b/benchmark/vm_poly_method_ov.yml
@@ -0,0 +1,24 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm_poly_method_ov: |
+ class C1
+ def m
+ 1
+ end
+ end
+ class C2
+ def m
+ 2
+ end
+ end
+
+ o1 = C1.new
+ o2 = C2.new
+
+ i = 0
+ while i<6_000_000
+ o = (i % 2 == 0) ? o1 : o2
+ # o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
+ i += 1
+ end
+loop_count: 1
diff --git a/benchmark/vm_poly_same_method.yml b/benchmark/vm_poly_same_method.yml
new file mode 100644
index 0000000000..6c5404ac84
--- /dev/null
+++ b/benchmark/vm_poly_same_method.yml
@@ -0,0 +1,25 @@
+prelude: |
+ module AR; end
+ class AR::Base
+ def create_or_update
+ nil
+ end
+ def save
+ create_or_update
+ end
+ end
+ class Foo < AR::Base; end
+ class Bar < AR::Base; end
+ o1 = Foo.new
+ o2 = Bar.new
+benchmark:
+ vm_poly_same_method: |
+ o1.save; o2.save;
+ o1.save; o2.save;
+ o1.save; o2.save;
+ o1.save; o2.save;
+ o1.save; o2.save;
+ o1.save; o2.save;
+ o1.save; o2.save;
+ o1.save; o2.save;
+loop_count: 6000000
diff --git a/benchmark/vm_poly_singleton.yml b/benchmark/vm_poly_singleton.yml
new file mode 100644
index 0000000000..c7923160fb
--- /dev/null
+++ b/benchmark/vm_poly_singleton.yml
@@ -0,0 +1,18 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm_poly_singleton: |
+ class C1
+ def m; 1; end
+ end
+
+ o1 = C1.new
+ o2 = C1.new
+ o2.singleton_class
+
+ i = 0
+ while i<6_000_000 # benchmark loop 2
+ o = (i % 2 == 0) ? o1 : o2
+ o.m; o.m; o.m; o.m; o.m; o.m; o.m; o.m
+ i += 1
+ end
+loop_count: 1
diff --git a/benchmark/vm_proc.yml b/benchmark/vm_proc.yml
new file mode 100644
index 0000000000..2f8de6c272
--- /dev/null
+++ b/benchmark/vm_proc.yml
@@ -0,0 +1,12 @@
+prelude: |
+ def m &b
+ b
+ end
+
+ pr = m{
+ a = 1
+ }
+benchmark:
+ vm_proc: |
+ pr.call
+loop_count: 6000000
diff --git a/benchmark/vm_raise1.yml b/benchmark/vm_raise1.yml
new file mode 100644
index 0000000000..247d9f70ee
--- /dev/null
+++ b/benchmark/vm_raise1.yml
@@ -0,0 +1,16 @@
+prelude: |
+ def rec n
+ if n > 0
+ rec n-1
+ else
+ raise
+ end
+ end
+benchmark:
+ vm_raise1: |
+ begin
+ rec 1
+ rescue
+ # ignore
+ end
+loop_count: 6000000
diff --git a/benchmark/vm_raise2.yml b/benchmark/vm_raise2.yml
new file mode 100644
index 0000000000..f0fa047b3c
--- /dev/null
+++ b/benchmark/vm_raise2.yml
@@ -0,0 +1,16 @@
+prelude: |
+ def rec n
+ if n > 0
+ rec n-1
+ else
+ raise
+ end
+ end
+benchmark:
+ vm_raise2: |
+ begin
+ rec 10
+ rescue
+ # ignore
+ end
+loop_count: 6000000
diff --git a/benchmark/vm_regexp.yml b/benchmark/vm_regexp.yml
new file mode 100644
index 0000000000..2aa3d94dbd
--- /dev/null
+++ b/benchmark/vm_regexp.yml
@@ -0,0 +1,8 @@
+prelude: |
+ str = 'xxxhogexxx'
+benchmark:
+ vm_regexp: |
+ /hoge/ =~ str
+ vm_regexp_invert: |
+ str =~ /hoge/
+loop_count: 6000000
diff --git a/benchmark/vm_rescue.yml b/benchmark/vm_rescue.yml
new file mode 100644
index 0000000000..b4a0af521f
--- /dev/null
+++ b/benchmark/vm_rescue.yml
@@ -0,0 +1,6 @@
+benchmark:
+ vm_rescue: |
+ begin
+ rescue
+ end
+loop_count: 30000000
diff --git a/benchmark/vm_send.yml b/benchmark/vm_send.yml
new file mode 100644
index 0000000000..f31bc7ac89
--- /dev/null
+++ b/benchmark/vm_send.yml
@@ -0,0 +1,14 @@
+prelude: |
+ class C
+ def m
+ end
+ end
+
+ o = C.new
+ m = :m
+benchmark:
+ vm_send: |
+ o.__send__ :m
+ vm_send_var: |
+ o.__send__ m
+loop_count: 6000000
diff --git a/benchmark/vm_send_cfunc.yml b/benchmark/vm_send_cfunc.yml
new file mode 100644
index 0000000000..b114ac317d
--- /dev/null
+++ b/benchmark/vm_send_cfunc.yml
@@ -0,0 +1,3 @@
+benchmark:
+ vm_send_cfunc: self.class
+loop_count: 100000000
diff --git a/benchmark/vm_simplereturn.yml b/benchmark/vm_simplereturn.yml
new file mode 100644
index 0000000000..c9829cff0b
--- /dev/null
+++ b/benchmark/vm_simplereturn.yml
@@ -0,0 +1,7 @@
+prelude: |
+ def m
+ return 1
+ end
+benchmark:
+ vm_simplereturn: m
+loop_count: 30000000
diff --git a/benchmark/vm_string_literal.yml b/benchmark/vm_string_literal.yml
new file mode 100644
index 0000000000..64439c7980
--- /dev/null
+++ b/benchmark/vm_string_literal.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm_string_literal: |
+ x = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+loop_count: 6000000
diff --git a/benchmark/vm_struct_big_aref_hi.yml b/benchmark/vm_struct_big_aref_hi.yml
new file mode 100644
index 0000000000..4cf78970cb
--- /dev/null
+++ b/benchmark/vm_struct_big_aref_hi.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+benchmark:
+ vm_struct_big_aref_hi: |
+ x.z # x[25]
+loop_count: 6000000
diff --git a/benchmark/vm_struct_big_aref_lo.yml b/benchmark/vm_struct_big_aref_lo.yml
new file mode 100644
index 0000000000..c91af27fa5
--- /dev/null
+++ b/benchmark/vm_struct_big_aref_lo.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+benchmark:
+ vm_struct_big_aref_lo: |
+ x.k # x[10]
+loop_count: 6000000
diff --git a/benchmark/vm_struct_big_aset.yml b/benchmark/vm_struct_big_aset.yml
new file mode 100644
index 0000000000..69550d14ea
--- /dev/null
+++ b/benchmark/vm_struct_big_aset.yml
@@ -0,0 +1,11 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm_struct_big_aset: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+ i = 0
+ while i<6_000_000
+ i += 1
+ x.k = i # x[10] = i
+ end
+loop_count: 1
diff --git a/benchmark/vm_struct_big_href_hi.yml b/benchmark/vm_struct_big_href_hi.yml
new file mode 100644
index 0000000000..09b764dd13
--- /dev/null
+++ b/benchmark/vm_struct_big_href_hi.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+benchmark:
+ vm_struct_big_href_hi: |
+ x[:z]
+loop_count: 6000000
diff --git a/benchmark/vm_struct_big_href_lo.yml b/benchmark/vm_struct_big_href_lo.yml
new file mode 100644
index 0000000000..d2f00b220f
--- /dev/null
+++ b/benchmark/vm_struct_big_href_lo.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+benchmark:
+ vm_struct_big_href_lo: |
+ x[:k]
+loop_count: 6000000
diff --git a/benchmark/vm_struct_big_hset.yml b/benchmark/vm_struct_big_hset.yml
new file mode 100644
index 0000000000..fc45cbee9c
--- /dev/null
+++ b/benchmark/vm_struct_big_hset.yml
@@ -0,0 +1,11 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm_struct_big_hset: |
+ s = Struct.new(*('a'..'z').map { |x| x.to_sym })
+ x = s.new
+ i = 0
+ while i<6_000_000
+ i += 1
+ x[:k] = i
+ end
+loop_count: 1
diff --git a/benchmark/vm_struct_small_aref.yml b/benchmark/vm_struct_small_aref.yml
new file mode 100644
index 0000000000..5a83251d1e
--- /dev/null
+++ b/benchmark/vm_struct_small_aref.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(:a, :b, :c)
+ x = s.new
+benchmark:
+ vm_struct_small_aref: |
+ x.a
+loop_count: 6000000
diff --git a/benchmark/vm_struct_small_aset.yml b/benchmark/vm_struct_small_aset.yml
new file mode 100644
index 0000000000..74f435f126
--- /dev/null
+++ b/benchmark/vm_struct_small_aset.yml
@@ -0,0 +1,11 @@
+# loop_count is not utilized since `i` is involved in the script
+benchmark:
+ vm_struct_small_aset: |
+ s = Struct.new(:a, :b, :c)
+ x = s.new
+ i = 0
+ while i<6_000_000
+ i += 1
+ x.a = i
+ end
+loop_count: 1
diff --git a/benchmark/vm_struct_small_href.yml b/benchmark/vm_struct_small_href.yml
new file mode 100644
index 0000000000..6b7d7f39e7
--- /dev/null
+++ b/benchmark/vm_struct_small_href.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(:a, :b, :c)
+ x = s.new
+benchmark:
+ vm_struct_small_href: |
+ x[:a]
+loop_count: 6000000
diff --git a/benchmark/vm_struct_small_hset.yml b/benchmark/vm_struct_small_hset.yml
new file mode 100644
index 0000000000..5d43b150de
--- /dev/null
+++ b/benchmark/vm_struct_small_hset.yml
@@ -0,0 +1,7 @@
+prelude: |
+ s = Struct.new(:a, :b, :c)
+ x = s.new
+benchmark:
+ vm_struct_small_hset: |
+ x[:a] = 1
+loop_count: 6000000
diff --git a/benchmark/vm_super.yml b/benchmark/vm_super.yml
new file mode 100644
index 0000000000..0d1e965c6e
--- /dev/null
+++ b/benchmark/vm_super.yml
@@ -0,0 +1,17 @@
+prelude: |
+ class C
+ def m
+ 1
+ end
+ end
+
+ class CC < C
+ def m
+ super()
+ end
+ end
+
+ obj = CC.new
+benchmark:
+ vm_super: obj.m
+loop_count: 6000000
diff --git a/benchmark/vm_swap.yml b/benchmark/vm_swap.yml
new file mode 100644
index 0000000000..e824a65e0a
--- /dev/null
+++ b/benchmark/vm_swap.yml
@@ -0,0 +1,7 @@
+prelude: |
+ a = 1
+ b = 2
+benchmark:
+ vm_swap: |
+ a, b = b, a
+loop_count: 30000000
diff --git a/benchmark/vm_thread_alive_check.yml b/benchmark/vm_thread_alive_check.yml
new file mode 100644
index 0000000000..d21737d3e8
--- /dev/null
+++ b/benchmark/vm_thread_alive_check.yml
@@ -0,0 +1,8 @@
+benchmark:
+ vm_thread_alive_check: |
+ t = Thread.new{}
+ while t.alive?
+ Thread.pass
+ end
+loop_count: 50_000
+
diff --git a/benchmark/vm_thread_alive_check1.rb b/benchmark/vm_thread_alive_check1.rb
deleted file mode 100644
index c993accdda..0000000000
--- a/benchmark/vm_thread_alive_check1.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-5_000.times{
- t = Thread.new{}
- while t.alive?
- Thread.pass
- end
-}
diff --git a/benchmark/vm_thread_condvar1.rb b/benchmark/vm_thread_condvar1.rb
index cf5706b23e..feed27c3ad 100644
--- a/benchmark/vm_thread_condvar1.rb
+++ b/benchmark/vm_thread_condvar1.rb
@@ -1,9 +1,9 @@
# two threads, two mutex, two condvar ping-pong
require 'thread'
-m1 = Mutex.new
-m2 = Mutex.new
-cv1 = ConditionVariable.new
-cv2 = ConditionVariable.new
+m1 = Thread::Mutex.new
+m2 = Thread::Mutex.new
+cv1 = Thread::ConditionVariable.new
+cv2 = Thread::ConditionVariable.new
max = 100000
i = 0
wait = nil
diff --git a/benchmark/vm_thread_condvar2.rb b/benchmark/vm_thread_condvar2.rb
index 7c8dc19481..6590c4134b 100644
--- a/benchmark/vm_thread_condvar2.rb
+++ b/benchmark/vm_thread_condvar2.rb
@@ -1,16 +1,16 @@
# many threads, one mutex, many condvars
require 'thread'
-m = Mutex.new
-cv1 = ConditionVariable.new
-cv2 = ConditionVariable.new
+m = Thread::Mutex.new
+cv1 = Thread::ConditionVariable.new
+cv2 = Thread::ConditionVariable.new
max = 1000
n = 100
waiting = 0
scvs = []
waiters = n.times.map do |i|
- start_cv = ConditionVariable.new
+ start_cv = Thread::ConditionVariable.new
scvs << start_cv
- start_mtx = Mutex.new
+ start_mtx = Thread::Mutex.new
start_mtx.synchronize do
th = Thread.new(start_mtx, start_cv) do |sm, scv|
m.synchronize do
diff --git a/benchmark/vm_thread_pass.rb b/benchmark/vm_thread_pass.rb
index b5b3c0bc85..438bd08d45 100644
--- a/benchmark/vm_thread_pass.rb
+++ b/benchmark/vm_thread_pass.rb
@@ -1,8 +1,8 @@
-# Plenty Thtread.pass
+# Plenty Thread.pass
# A performance may depend on GVL implementation.
-tmax = (ARGV.shift || 2).to_i
-lmax = 200_000 / tmax
+tmax = (ARGV.shift || 8).to_i
+lmax = 400_000 / tmax
(1..tmax).map{
Thread.new{
diff --git a/benchmark/vm_thread_pass_flood.rb b/benchmark/vm_thread_pass_flood.rb
index a660aafc18..65df8e6154 100644
--- a/benchmark/vm_thread_pass_flood.rb
+++ b/benchmark/vm_thread_pass_flood.rb
@@ -1,10 +1,10 @@
# n.b. this is a good test for GVL when pinned to a single CPU
-1000.times{
+5_000.times{
Thread.new{loop{Thread.pass}}
}
i = 0
-while i<10000
+while i<10_000
i += 1
end
diff --git a/benchmark/vm_thread_queue.rb b/benchmark/vm_thread_queue.rb
index 274ceda366..1dd3696a3c 100644
--- a/benchmark/vm_thread_queue.rb
+++ b/benchmark/vm_thread_queue.rb
@@ -1,6 +1,6 @@
require 'thread'
-n = 1_000_000
+n = 10_000_000
q = Thread::Queue.new
consumer = Thread.new{
while q.pop
diff --git a/benchmark/vm_thread_sleep.yml b/benchmark/vm_thread_sleep.yml
new file mode 100644
index 0000000000..96901d8466
--- /dev/null
+++ b/benchmark/vm_thread_sleep.yml
@@ -0,0 +1,4 @@
+benchmark:
+ vm_thread_sleep: |
+ Thread.new { sleep }
+loop_count: 10_000
diff --git a/benchmark/vm_unif1.yml b/benchmark/vm_unif1.yml
new file mode 100644
index 0000000000..04187bb0e2
--- /dev/null
+++ b/benchmark/vm_unif1.yml
@@ -0,0 +1,7 @@
+prelude: |
+ def m a, b
+ end
+benchmark:
+ vm_unif1: |
+ m 100, 200
+loop_count: 6000000
diff --git a/benchmark/vm_yield.yml b/benchmark/vm_yield.yml
new file mode 100644
index 0000000000..230be3d84f
--- /dev/null
+++ b/benchmark/vm_yield.yml
@@ -0,0 +1,13 @@
+# while loop cost is not removed due to benchmark_driver.gem's limitation
+benchmark:
+ vm_yield: |
+ def m
+ i = 0
+ while i<30_000_000
+ i += 1
+ yield
+ end
+ end
+
+ m{}
+loop_count: 1
diff --git a/benchmark/vm_zsuper.yml b/benchmark/vm_zsuper.yml
new file mode 100644
index 0000000000..bfb5837578
--- /dev/null
+++ b/benchmark/vm_zsuper.yml
@@ -0,0 +1,18 @@
+prelude: |
+ class C
+ def m a
+ 1
+ end
+ end
+
+ class CC < C
+ def m a
+ super
+ end
+ end
+
+ obj = CC.new
+benchmark:
+ vm_zsuper: |
+ obj.m 10
+loop_count: 6000000
diff --git a/bignum.c b/bignum.c
index ee481e1bd7..cb2c3b6f07 100644
--- a/bignum.c
+++ b/bignum.c
@@ -9,32 +9,45 @@
**********************************************************************/
-#include "internal.h"
-#include "ruby/thread.h"
-#include "ruby/util.h"
-#include "id.h"
+#include "ruby/internal/config.h"
+
+#include <ctype.h>
+#include <float.h>
+#include <math.h>
#ifdef HAVE_STRINGS_H
-#include <strings.h>
+# include <strings.h>
#endif
-#include <math.h>
-#include <float.h>
-#include <ctype.h>
+
#ifdef HAVE_IEEEFP_H
-#include <ieeefp.h>
+# include <ieeefp.h>
#endif
-#include "ruby_assert.h"
+#if !defined(USE_GMP)
#if defined(HAVE_LIBGMP) && defined(HAVE_GMP_H)
-#define USE_GMP
-#include <gmp.h>
+# define USE_GMP 1
+#else
+# define USE_GMP 0
+#endif
+#endif
+#if USE_GMP
+# include <gmp.h>
#endif
-#define RB_BIGNUM_TYPE_P(x) RB_TYPE_P((x), T_BIGNUM)
+#include "id.h"
+#include "internal.h"
+#include "internal/bignum.h"
+#include "internal/complex.h"
+#include "internal/gc.h"
+#include "internal/numeric.h"
+#include "internal/object.h"
+#include "internal/sanitizers.h"
+#include "internal/variable.h"
+#include "internal/warnings.h"
+#include "ruby/thread.h"
+#include "ruby/util.h"
+#include "ruby_assert.h"
-#ifndef RUBY_INTEGER_UNIFICATION
-VALUE rb_cBignum;
-#endif
const char ruby_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
#ifndef SIZEOF_BDIGIT_DBL
@@ -65,7 +78,7 @@ STATIC_ASSERT(sizeof_long_and_sizeof_bdigit, SIZEOF_BDIGIT % SIZEOF_LONG == 0);
#else
# define HOST_BIGENDIAN_P 0
#endif
-/* (!LSHIFTABLE(d, n) ? 0 : (n)) is same as n but suppress a warning, C4293, by Visual Studio. */
+/* (!LSHIFTABLE(d, n) ? 0 : (n)) is the same as n but suppress a warning, C4293, by Visual Studio. */
#define LSHIFTABLE(d, n) ((n) < sizeof(d) * CHAR_BIT)
#define LSHIFTX(d, n) (!LSHIFTABLE(d, n) ? 0 : ((d) << (!LSHIFTABLE(d, n) ? 0 : (n))))
#define CLEAR_LOWBITS(d, numbits) ((d) & LSHIFTX(~((d)*0), (numbits)))
@@ -92,8 +105,8 @@ STATIC_ASSERT(sizeof_long_and_sizeof_bdigit, SIZEOF_BDIGIT % SIZEOF_LONG == 0);
#endif
#define BIGZEROP(x) (BIGNUM_LEN(x) == 0 || \
- (BDIGITS(x)[0] == 0 && \
- (BIGNUM_LEN(x) == 1 || bigzero_p(x))))
+ (BDIGITS(x)[0] == 0 && \
+ (BIGNUM_LEN(x) == 1 || bigzero_p(x))))
#define BIGSIZE(x) (BIGNUM_LEN(x) == 0 ? (size_t)0 : \
BDIGITS(x)[BIGNUM_LEN(x)-1] ? \
(size_t)(BIGNUM_LEN(x)*SIZEOF_BDIGIT - nlz(BDIGITS(x)[BIGNUM_LEN(x)-1])/CHAR_BIT) : \
@@ -138,7 +151,7 @@ STATIC_ASSERT(sizeof_long_and_sizeof_bdigit, SIZEOF_BDIGIT % SIZEOF_LONG == 0);
#define GMP_DIV_DIGITS 20
#define GMP_BIG2STR_DIGITS 20
#define GMP_STR2BIG_DIGITS 20
-#ifdef USE_GMP
+#if USE_GMP
# define NAIVE_MUL_DIGITS GMP_MUL_DIGITS
#else
# define NAIVE_MUL_DIGITS KARATSUBA_MUL_DIGITS
@@ -149,15 +162,11 @@ typedef void (mulfunc_t)(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, c
static mulfunc_t bary_mul_toom3_start;
static mulfunc_t bary_mul_karatsuba_start;
static BDIGIT bigdivrem_single(BDIGIT *qds, const BDIGIT *xds, size_t xn, BDIGIT y);
-static void bary_divmod(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn);
-static VALUE bigmul0(VALUE x, VALUE y);
-static void bary_mul_toom3(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn, BDIGIT *wds, size_t wn);
static VALUE bignew_1(VALUE klass, size_t len, int sign);
static inline VALUE bigtrunc(VALUE x);
static VALUE bigsq(VALUE x);
-static void bigdivmod(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp);
static inline VALUE power_cache_get_power(int base, int power_level, size_t *numdigits_ret);
#if SIZEOF_BDIGIT <= SIZEOF_INT
@@ -385,6 +394,7 @@ bdigitdbl2bary(BDIGIT *ds, size_t n, BDIGIT_DBL num)
static int
bary_cmp(const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
{
+ size_t i;
BARY_TRUNC(xds, xn);
BARY_TRUNC(yds, yn);
@@ -393,11 +403,12 @@ bary_cmp(const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
if (xn > yn)
return 1;
- while (xn-- && xds[xn] == yds[xn])
- ;
- if (xn == (size_t)-1)
+ for (i = 0; i < xn; i++)
+ if (xds[xn - i - 1] != yds[yn - i - 1])
+ break;
+ if (i == xn)
return 0;
- return xds[xn] < yds[xn] ? -1 : 1;
+ return xds[xn - i - 1] < yds[yn - i - 1] ? -1 : 1;
}
static BDIGIT
@@ -408,9 +419,9 @@ bary_small_lshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift)
assert(0 <= shift && shift < BITSPERDIG);
for (i=0; i<n; i++) {
- num = num | (BDIGIT_DBL)*xds++ << shift;
- *zds++ = BIGLO(num);
- num = BIGDN(num);
+ num = num | (BDIGIT_DBL)*xds++ << shift;
+ *zds++ = BIGLO(num);
+ num = BIGDN(num);
}
return BIGLO(num);
}
@@ -418,16 +429,17 @@ bary_small_lshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift)
static void
bary_small_rshift(BDIGIT *zds, const BDIGIT *xds, size_t n, int shift, BDIGIT higher_bdigit)
{
+ size_t i;
BDIGIT_DBL num = 0;
assert(0 <= shift && shift < BITSPERDIG);
num = BIGUP(higher_bdigit);
- while (n--) {
- BDIGIT x = xds[n];
- num = (num | x) >> shift;
- zds[n] = BIGLO(num);
- num = BIGUP(x);
+ for (i = 0; i < n; i++) {
+ BDIGIT x = xds[n - i - 1];
+ num = (num | x) >> shift;
+ zds[n - i - 1] = BIGLO(num);
+ num = BIGUP(x);
}
}
@@ -437,7 +449,7 @@ bary_zero_p(const BDIGIT *xds, size_t xn)
if (xn == 0)
return 1;
do {
- if (xds[--xn]) return 0;
+ if (xds[--xn]) return 0;
} while (xn);
return 1;
}
@@ -445,15 +457,15 @@ bary_zero_p(const BDIGIT *xds, size_t xn)
static void
bary_neg(BDIGIT *ds, size_t n)
{
- while (n--)
- ds[n] = BIGLO(~ds[n]);
+ size_t i;
+ for (i = 0; i < n; i++)
+ ds[n - i - 1] = BIGLO(~ds[n - i - 1]);
}
static int
bary_2comp(BDIGIT *ds, size_t n)
{
size_t i;
- i = 0;
for (i = 0; i < n; i++) {
if (ds[i] != 0) {
goto non_zero;
@@ -616,8 +628,12 @@ static int
bytes_2comp(unsigned char *buf, size_t len)
{
size_t i;
- for (i = 0; i < len; i++)
- buf[i] = ~buf[i];
+ for (i = 0; i < len; i++) {
+ signed char c = buf[i];
+ signed int d = ~c;
+ unsigned int e = d & 0xFF;
+ buf[i] = e;
+ }
for (i = 0; i < len; i++) {
buf[i]++;
if (buf[i] != 0)
@@ -961,7 +977,7 @@ integer_unpack_num_bdigits_small(size_t numwords, size_t wordsize, size_t nails,
{
/* nlp_bits stands for number of leading padding bits */
size_t num_bits = (wordsize * CHAR_BIT - nails) * numwords;
- size_t num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG;
+ size_t num_bdigits = roomof(num_bits, BITSPERDIG);
*nlp_bits_ret = (int)(num_bdigits * BITSPERDIG - num_bits);
return num_bdigits;
}
@@ -971,7 +987,7 @@ integer_unpack_num_bdigits_generic(size_t numwords, size_t wordsize, size_t nail
{
/* BITSPERDIG = SIZEOF_BDIGIT * CHAR_BIT */
/* num_bits = (wordsize * CHAR_BIT - nails) * numwords */
- /* num_bdigits = (num_bits + BITSPERDIG - 1) / BITSPERDIG */
+ /* num_bdigits = roomof(num_bits, BITSPERDIG) */
/* num_bits = CHAR_BIT * (wordsize * numwords) - nails * numwords = CHAR_BIT * num_bytes1 - nails * numwords */
size_t num_bytes1 = wordsize * numwords;
@@ -1039,6 +1055,7 @@ integer_unpack_num_bdigits(size_t numwords, size_t wordsize, size_t nails, int *
size_t num_bdigits1 = integer_unpack_num_bdigits_generic(numwords, wordsize, nails, &nlp_bits1);
assert(num_bdigits == num_bdigits1);
assert(*nlp_bits_ret == nlp_bits1);
+ (void)num_bdigits1;
}
#endif
}
@@ -1333,9 +1350,9 @@ bary_subb(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
num = borrow ? -1 : 0;
for (i = 0; i < sn; i++) {
- num += (BDIGIT_DBL_SIGNED)xds[i] - yds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += (BDIGIT_DBL_SIGNED)xds[i] - yds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
if (yn <= xn) {
for (; i < xn; i++) {
@@ -1354,7 +1371,7 @@ bary_subb(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
}
if (num == 0) goto num_is_zero;
for (; i < zn; i++) {
- zds[i] = BDIGMAX;
+ zds[i] = BDIGMAX;
}
return 1;
@@ -1362,10 +1379,10 @@ bary_subb(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
if (xds == zds && xn == zn)
return 0;
for (; i < xn; i++) {
- zds[i] = xds[i];
+ zds[i] = xds[i];
}
for (; i < zn; i++) {
- zds[i] = 0;
+ zds[i] = 0;
}
return 0;
}
@@ -1392,27 +1409,27 @@ bary_addc(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
assert(yn <= zn);
if (xn > yn) {
- const BDIGIT *tds;
- tds = xds; xds = yds; yds = tds;
- i = xn; xn = yn; yn = i;
+ const BDIGIT *tds;
+ tds = xds; xds = yds; yds = tds;
+ i = xn; xn = yn; yn = i;
}
num = carry ? 1 : 0;
for (i = 0; i < xn; i++) {
- num += (BDIGIT_DBL)xds[i] + yds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += (BDIGIT_DBL)xds[i] + yds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
for (; i < yn; i++) {
if (num == 0) goto num_is_zero;
- num += yds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += yds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
for (; i < zn; i++) {
if (num == 0) goto num_is_zero;
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
return num != 0;
@@ -1420,10 +1437,10 @@ bary_addc(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yd
if (yds == zds && yn == zn)
return 0;
for (; i < yn; i++) {
- zds[i] = yds[i];
+ zds[i] = yds[i];
}
for (; i < zn; i++) {
- zds[i] = 0;
+ zds[i] = 0;
}
return 0;
}
@@ -1439,7 +1456,9 @@ bary_add_one(BDIGIT *ds, size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
- ds[i] = BIGLO(ds[i]+1);
+ BDIGIT_DBL n = ds[i];
+ n += 1;
+ ds[i] = BIGLO(n);
if (ds[i] != 0)
return 0;
}
@@ -1507,15 +1526,16 @@ bigdivrem_mulsub(BDIGIT *zds, size_t zn, BDIGIT x, const BDIGIT *yds, size_t yn)
i = 0;
do {
- BDIGIT_DBL ee;
+ BDIGIT_DBL_SIGNED ee;
t2 += (BDIGIT_DBL)yds[i] * x;
ee = num - BIGLO(t2);
- num = (BDIGIT_DBL)zds[i] + ee;
+ num = (BDIGIT_DBL_SIGNED)zds[i] + ee;
if (ee) zds[i] = BIGLO(num);
num = BIGDN(num);
t2 = BIGDN(t2);
} while (++i < yn);
- num += zds[i] - t2; /* borrow from high digit; don't update */
+ num -= (BDIGIT_DBL_SIGNED)t2;
+ num += (BDIGIT_DBL_SIGNED)zds[yn]; /* borrow from high digit; don't update */
return num;
}
@@ -1559,7 +1579,7 @@ rb_big_mul_normal(VALUE x, VALUE y)
/* efficient squaring (2 times faster than normal multiplication)
* ref: Handbook of Applied Cryptography, Algorithm 14.16
- * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
+ * https://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
*/
static void
bary_sq_fast(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn)
@@ -1577,30 +1597,30 @@ bary_sq_fast(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn)
return;
for (i = 0; i < xn-1; i++) {
- v = (BDIGIT_DBL)xds[i];
- if (!v)
+ v = (BDIGIT_DBL)xds[i];
+ if (!v)
continue;
- c = (BDIGIT_DBL)zds[i + i] + v * v;
- zds[i + i] = BIGLO(c);
- c = BIGDN(c);
- v *= 2;
+ c = (BDIGIT_DBL)zds[i + i] + v * v;
+ zds[i + i] = BIGLO(c);
+ c = BIGDN(c);
+ v *= 2;
vl = BIGLO(v);
vh = (int)BIGDN(v);
- for (j = i + 1; j < xn; j++) {
- w = (BDIGIT_DBL)xds[j];
- c += (BDIGIT_DBL)zds[i + j] + vl * w;
- zds[i + j] = BIGLO(c);
- c = BIGDN(c);
- if (vh)
+ for (j = i + 1; j < xn; j++) {
+ w = (BDIGIT_DBL)xds[j];
+ c += (BDIGIT_DBL)zds[i + j] + vl * w;
+ zds[i + j] = BIGLO(c);
+ c = BIGDN(c);
+ if (vh)
c += w;
- }
- if (c) {
- c += (BDIGIT_DBL)zds[i + xn];
- zds[i + xn] = BIGLO(c);
- c = BIGDN(c);
+ }
+ if (c) {
+ c += (BDIGIT_DBL)zds[i + xn];
+ zds[i + xn] = BIGLO(c);
+ c = BIGDN(c);
if (c)
zds[i + xn + 1] += (BDIGIT)c;
- }
+ }
}
/* i == xn-1 */
@@ -1625,13 +1645,21 @@ rb_big_sq_fast(VALUE x)
return z;
}
+static inline size_t
+max_size(size_t a, size_t b)
+{
+ return (a > b ? a : b);
+}
+
/* balancing multiplication by slicing larger argument */
static void
-bary_mul_balance_with_mulfunc(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn, BDIGIT *wds, size_t wn, mulfunc_t *mulfunc)
+bary_mul_balance_with_mulfunc(BDIGIT *const zds, const size_t zn,
+ const BDIGIT *const xds, const size_t xn,
+ const BDIGIT *const yds, const size_t yn,
+ BDIGIT *wds, size_t wn, mulfunc_t *const mulfunc)
{
VALUE work = 0;
- size_t yn0 = yn;
- size_t r, n;
+ size_t n;
assert(xn + yn <= zn);
assert(xn <= yn);
@@ -1639,14 +1667,26 @@ bary_mul_balance_with_mulfunc(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t
BDIGITS_ZERO(zds, xn);
+ if (wn < xn) {
+ /* The condition when a new buffer is needed:
+ * 1. (2(xn+r) > zn-(yn-r)) => (2xn+r > zn-yn), at the last
+ * iteration (or r == 0)
+ * 2. (2(xn+xn) > zn-(yn-r-xn)) => (3xn-r > zn-yn), at the
+ * previous iteration.
+ */
+ const size_t r = yn % xn;
+ if (2*xn + yn + max_size(xn-r, r) > zn) {
+ wn = xn;
+ wds = ALLOCV_N(BDIGIT, work, wn);
+ }
+ }
+
n = 0;
- while (yn > 0) {
- BDIGIT *tds;
- size_t tn;
- r = xn > yn ? yn : xn;
- tn = xn + r;
+ while (yn > n) {
+ const size_t r = (xn > (yn - n) ? (yn - n) : xn);
+ const size_t tn = (xn + r);
if (2 * (xn + r) <= zn - n) {
- tds = zds + n + xn + r;
+ BDIGIT *const tds = zds + n + xn + r;
mulfunc(tds, tn, xds, xn, yds + n, r, wds, wn);
BDIGITS_ZERO(zds + n + xn, r);
bary_add(zds + n, tn,
@@ -1654,21 +1694,25 @@ bary_mul_balance_with_mulfunc(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t
tds, tn);
}
else {
+ BDIGIT *const tds = zds + n;
if (wn < xn) {
+ /* xn is invariant, only once here */
+#if 0
wn = xn;
wds = ALLOCV_N(BDIGIT, work, wn);
+#else
+ rb_bug("wds is not enough: %" PRIdSIZE " for %" PRIdSIZE, wn, xn);
+#endif
}
- tds = zds + n;
MEMCPY(wds, zds + n, BDIGIT, xn);
mulfunc(tds, tn, xds, xn, yds + n, r, wds+xn, wn-xn);
bary_add(zds + n, tn,
zds + n, tn,
wds, xn);
}
- yn -= r;
- n += r;
+ n += r;
}
- BDIGITS_ZERO(zds+xn+yn0, zn - (xn+yn0));
+ BDIGITS_ZERO(zds+xn+yn, zn - (xn+yn));
if (work)
ALLOCV_END(work);
@@ -1985,7 +2029,7 @@ bary_mul_toom3(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGI
}
/*
- * ref. http://en.wikipedia.org/wiki/Toom%E2%80%93Cook_multiplication
+ * ref. https://en.wikipedia.org/wiki/Toom%E2%80%93Cook_multiplication
*
* x(b) = x0 * b^0 + x1 * b^1 + x2 * b^2
* y(b) = y0 * b^0 + y1 * b^1 + y2 * b^2
@@ -2058,21 +2102,21 @@ bary_mul_toom3(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGI
v3n = u3n; v3ds = u3ds; v3p = u3p;
}
else {
- /* v1 <- y0 + y2 */
+ /* v1 <- y0 + y2 */
bary_add(v1ds, v1n, y0ds, y0n, y2ds, y2n);
v1p = 1;
- /* y(-1) : v2 <- v1 - y1 = y0 - y1 + y2 */
+ /* y(-1) : v2 <- v1 - y1 = y0 - y1 + y2 */
v2p = 1;
if (bary_sub(v2ds, v2n, v1ds, v1n, y1ds, y1n)) {
bary_2comp(v2ds, v2n);
v2p = 0;
}
- /* y(1) : v1 <- v1 + y1 = y0 + y1 + y2 */
+ /* y(1) : v1 <- v1 + y1 = y0 + y1 + y2 */
bary_add(v1ds, v1n, v1ds, v1n, y1ds, y1n);
- /* y(-2) : v3 <- 2 * (v2 + y2) - y0 = y0 - 2 * (y1 - 2 * y2) */
+ /* y(-2) : v3 <- 2 * (v2 + y2) - y0 = y0 - 2 * (y1 - 2 * y2) */
v3p = 1;
if (v2p) {
bary_add(v3ds, v3n, v2ds, v2n, y2ds, y2n);
@@ -2265,11 +2309,24 @@ rb_big_mul_toom3(VALUE x, VALUE y)
return z;
}
-#ifdef USE_GMP
+#if USE_GMP
+static inline void
+bdigits_to_mpz(mpz_t mp, const BDIGIT *digits, size_t len)
+{
+ const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT;
+ mpz_import(mp, len, -1, sizeof(BDIGIT), 0, nails, digits);
+}
+
+static inline void
+bdigits_from_mpz(mpz_t mp, BDIGIT *digits, size_t *len)
+{
+ const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT;
+ mpz_export(digits, len, -1, sizeof(BDIGIT), 0, nails, mp);
+}
+
static void
bary_mul_gmp(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
{
- const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT;
mpz_t x, y, z;
size_t count;
@@ -2278,15 +2335,15 @@ bary_mul_gmp(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT
mpz_init(x);
mpz_init(y);
mpz_init(z);
- mpz_import(x, xn, -1, sizeof(BDIGIT), 0, nails, xds);
+ bdigits_to_mpz(x, xds, xn);
if (xds == yds && xn == yn) {
mpz_mul(z, x, x);
}
else {
- mpz_import(y, yn, -1, sizeof(BDIGIT), 0, nails, yds);
+ bdigits_to_mpz(y, yds, yn);
mpz_mul(z, x, y);
}
- mpz_export(zds, &count, -1, sizeof(BDIGIT), 0, nails, z);
+ bdigits_from_mpz(z, zds, &count);
BDIGITS_ZERO(zds+count, zn-count);
mpz_clear(x);
mpz_clear(y);
@@ -2325,9 +2382,9 @@ bary_sparse_p(const BDIGIT *ds, size_t n)
{
long c = 0;
- if ( ds[rb_genrand_ulong_limited(n / 2) + n / 4]) c++;
- if (c <= 1 && ds[rb_genrand_ulong_limited(n / 2) + n / 4]) c++;
- if (c <= 1 && ds[rb_genrand_ulong_limited(n / 2) + n / 4]) c++;
+ if ( ds[2 * n / 5]) c++;
+ if (c <= 1 && ds[ n / 2]) c++;
+ if (c <= 1 && ds[3 * n / 5]) c++;
return (c <= 1) ? 1 : 0;
}
@@ -2390,8 +2447,8 @@ bary_mul_precheck(BDIGIT **zdsp, size_t *znp, const BDIGIT **xdsp, size_t *xnp,
if (xn > yn) {
const BDIGIT *tds;
size_t tn;
- tds = xds; xds = yds; yds = tds;
- tn = xn; xn = yn; yn = tn;
+ tds = xds; xds = yds; yds = tds;
+ tn = xn; xn = yn; yn = tn;
}
assert(xn <= yn);
@@ -2435,12 +2492,7 @@ bary_mul_karatsuba_branch(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn,
{
/* normal multiplication when x is small */
if (xn < KARATSUBA_MUL_DIGITS) {
- normal:
- if (xds == yds && xn == yn)
- bary_sq_fast(zds, zn, xds, xn);
- else
- bary_short_mul(zds, zn, xds, xn, yds, yn);
- return;
+ goto normal;
}
/* normal multiplication when x or y is a sparse bignum */
@@ -2458,6 +2510,15 @@ bary_mul_karatsuba_branch(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn,
/* multiplication by karatsuba method */
bary_mul_karatsuba(zds, zn, xds, xn, yds, yn, wds, wn);
+ return;
+
+ normal:
+ if (xds == yds && xn == yn) {
+ bary_sq_fast(zds, zn, xds, xn);
+ }
+ else {
+ bary_short_mul(zds, zn, xds, xn, yds, yn);
+ }
}
static void
@@ -2513,7 +2574,7 @@ bary_mul(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds
}
}
-#ifdef USE_GMP
+#if USE_GMP
bary_mul_gmp(zds, zn, xds, xn, yds, yn);
#else
bary_mul_toom3_start(zds, zn, xds, xn, yds, yn, NULL, 0);
@@ -2537,30 +2598,31 @@ bigdivrem1(void *ptr)
BDIGIT q;
do {
- if (bds->stop) {
- bds->zn = zn;
- return 0;
+ if (bds->stop) {
+ bds->zn = zn;
+ return 0;
}
- if (zds[zn-1] == yds[yn-1]) q = BDIGMAX;
- else q = (BDIGIT)((BIGUP(zds[zn-1]) + zds[zn-2])/yds[yn-1]);
- if (q) {
+ if (zds[zn-1] == yds[yn-1]) q = BDIGMAX;
+ else q = (BDIGIT)((BIGUP(zds[zn-1]) + zds[zn-2])/yds[yn-1]);
+ if (q) {
num = bigdivrem_mulsub(zds+zn-(yn+1), yn+1,
q,
yds, yn);
- while (num) { /* "add back" required */
- q--;
+ while (num) { /* "add back" required */
+ q--;
num = bary_add(zds+zn-(yn+1), yn,
zds+zn-(yn+1), yn,
yds, yn);
num--;
- }
- }
+ }
+ }
zn--;
- zds[zn] = q;
+ zds[zn] = q;
} while (zn > yn);
return 0;
}
+/* async-signal-safe */
static void
rb_big_stop(void *ptr)
{
@@ -2583,10 +2645,9 @@ bigdivrem_single1(BDIGIT *qds, const BDIGIT *xds, size_t xn, BDIGIT x_higher_bdi
size_t i;
BDIGIT_DBL t2;
t2 = x_higher_bdigit;
- i = xn;
- while (i--) {
- t2 = BIGUP(t2) + xds[i];
- qds[i] = (BDIGIT)(t2 / y);
+ for (i = 0; i < xn; i++) {
+ t2 = BIGUP(t2) + xds[xn - i - 1];
+ qds[xn - i - 1] = (BDIGIT)(t2 / y);
t2 %= y;
}
return (BDIGIT)t2;
@@ -2625,16 +2686,16 @@ bigdivrem_restoring(BDIGIT *zds, size_t zn, BDIGIT *yds, size_t yn)
bds.zn = zn - ynzero;
if (bds.zn > 10000 || bds.yn > 10000) {
retry:
- bds.stop = Qfalse;
- rb_thread_call_without_gvl(bigdivrem1, &bds, rb_big_stop, &bds);
+ bds.stop = Qfalse;
+ rb_nogvl(bigdivrem1, &bds, rb_big_stop, &bds, RB_NOGVL_UBF_ASYNC_SAFE);
- if (bds.stop == Qtrue) {
- /* execute trap handler, but exception was not raised. */
- goto retry;
- }
+ if (bds.stop == Qtrue) {
+ /* execute trap handler, but exception was not raised. */
+ goto retry;
+ }
}
else {
- bigdivrem1(&bds);
+ bigdivrem1(&bds);
}
}
@@ -2733,11 +2794,10 @@ rb_big_divrem_normal(VALUE x, VALUE y)
return rb_assoc_new(q, r);
}
-#ifdef USE_GMP
+#if USE_GMP
static void
bary_divmod_gmp(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
{
- const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT;
mpz_t x, y, q, r;
size_t count;
@@ -2751,8 +2811,8 @@ bary_divmod_gmp(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT *xd
if (qds) mpz_init(q);
if (rds) mpz_init(r);
- mpz_import(x, xn, -1, sizeof(BDIGIT), 0, nails, xds);
- mpz_import(y, yn, -1, sizeof(BDIGIT), 0, nails, yds);
+ bdigits_to_mpz(x, xds, xn);
+ bdigits_to_mpz(y, yds, yn);
if (!rds) {
mpz_fdiv_q(q, x, y);
@@ -2768,13 +2828,13 @@ bary_divmod_gmp(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT *xd
mpz_clear(y);
if (qds) {
- mpz_export(qds, &count, -1, sizeof(BDIGIT), 0, nails, q);
+ bdigits_from_mpz(q, qds, &count);
BDIGITS_ZERO(qds+count, qn-count);
mpz_clear(q);
}
if (rds) {
- mpz_export(rds, &count, -1, sizeof(BDIGIT), 0, nails, r);
+ bdigits_from_mpz(r, rds, &count);
BDIGITS_ZERO(rds+count, rn-count);
mpz_clear(r);
}
@@ -2818,7 +2878,7 @@ rb_big_divrem_gmp(VALUE x, VALUE y)
static void
bary_divmod_branch(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn)
{
-#ifdef USE_GMP
+#if USE_GMP
if (GMP_DIV_DIGITS < xn) {
bary_divmod_gmp(qds, qn, rds, rn, xds, xn, yds, yn);
return;
@@ -2873,29 +2933,8 @@ bary_divmod(BDIGIT *qds, size_t qn, BDIGIT *rds, size_t rn, const BDIGIT *xds, s
}
-#define BIGNUM_DEBUG 0
-#if BIGNUM_DEBUG
-#define ON_DEBUG(x) do { x; } while (0)
-static void
-dump_bignum(VALUE x)
-{
- long i;
- printf("%c0x0", BIGNUM_SIGN(x) ? '+' : '-');
- for (i = BIGNUM_LEN(x); i--; ) {
- printf("_%0*"PRIxBDIGIT, SIZEOF_BDIGIT*2, BDIGITS(x)[i]);
- }
- printf(", len=%"PRIuSIZE, BIGNUM_LEN(x));
- puts("");
-}
-
-static VALUE
-rb_big_dump(VALUE x)
-{
- dump_bignum(x);
- return x;
-}
-#else
-#define ON_DEBUG(x)
+#ifndef BIGNUM_DEBUG
+# define BIGNUM_DEBUG (0+RUBY_DEBUG)
#endif
static int
@@ -2914,7 +2953,7 @@ int
rb_cmpint(VALUE val, VALUE a, VALUE b)
{
if (NIL_P(val)) {
- rb_cmperr(a, b);
+ rb_cmperr(a, b);
}
if (FIXNUM_P(val)) {
long l = FIX2LONG(val);
@@ -2923,9 +2962,9 @@ rb_cmpint(VALUE val, VALUE a, VALUE b)
return 0;
}
if (RB_BIGNUM_TYPE_P(val)) {
- if (BIGZEROP(val)) return 0;
- if (BIGNUM_SIGN(val)) return 1;
- return -1;
+ if (BIGZEROP(val)) return 0;
+ if (BIGNUM_SIGN(val)) return 1;
+ return -1;
}
if (RTEST(rb_funcall(val, '>', 1, INT2FIX(0)))) return 1;
if (RTEST(rb_funcall(val, '<', 1, INT2FIX(0)))) return -1;
@@ -2933,44 +2972,44 @@ rb_cmpint(VALUE val, VALUE a, VALUE b)
}
#define BIGNUM_SET_LEN(b,l) \
- ((RBASIC(b)->flags & BIGNUM_EMBED_FLAG) ? \
+ (BIGNUM_EMBED_P(b) ? \
(void)(RBASIC(b)->flags = \
- (RBASIC(b)->flags & ~BIGNUM_EMBED_LEN_MASK) | \
- ((l) << BIGNUM_EMBED_LEN_SHIFT)) : \
+ (RBASIC(b)->flags & ~BIGNUM_EMBED_LEN_MASK) | \
+ ((l) << BIGNUM_EMBED_LEN_SHIFT)) : \
(void)(RBIGNUM(b)->as.heap.len = (l)))
static void
rb_big_realloc(VALUE big, size_t len)
{
BDIGIT *ds;
- if (RBASIC(big)->flags & BIGNUM_EMBED_FLAG) {
- if (BIGNUM_EMBED_LEN_MAX < len) {
- ds = ALLOC_N(BDIGIT, len);
- MEMCPY(ds, RBIGNUM(big)->as.ary, BDIGIT, BIGNUM_EMBED_LEN_MAX);
- RBIGNUM(big)->as.heap.len = BIGNUM_LEN(big);
- RBIGNUM(big)->as.heap.digits = ds;
- RBASIC(big)->flags &= ~BIGNUM_EMBED_FLAG;
- }
+ if (BIGNUM_EMBED_P(big)) {
+ if (BIGNUM_EMBED_LEN_MAX < len) {
+ ds = ALLOC_N(BDIGIT, len);
+ MEMCPY(ds, RBIGNUM(big)->as.ary, BDIGIT, BIGNUM_EMBED_LEN_MAX);
+ RBIGNUM(big)->as.heap.len = BIGNUM_LEN(big);
+ RBIGNUM(big)->as.heap.digits = ds;
+ FL_UNSET_RAW(big, BIGNUM_EMBED_FLAG);
+ }
}
else {
- if (len <= BIGNUM_EMBED_LEN_MAX) {
- ds = RBIGNUM(big)->as.heap.digits;
- RBASIC(big)->flags |= BIGNUM_EMBED_FLAG;
- BIGNUM_SET_LEN(big, len);
+ if (len <= BIGNUM_EMBED_LEN_MAX) {
+ ds = RBIGNUM(big)->as.heap.digits;
+ FL_SET_RAW(big, BIGNUM_EMBED_FLAG);
+ BIGNUM_SET_LEN(big, len);
(void)VALGRIND_MAKE_MEM_UNDEFINED((void*)RBIGNUM(big)->as.ary, sizeof(RBIGNUM(big)->as.ary));
- if (ds) {
- MEMCPY(RBIGNUM(big)->as.ary, ds, BDIGIT, len);
- xfree(ds);
- }
- }
- else {
- if (BIGNUM_LEN(big) == 0) {
- RBIGNUM(big)->as.heap.digits = ALLOC_N(BDIGIT, len);
- }
- else {
- REALLOC_N(RBIGNUM(big)->as.heap.digits, BDIGIT, len);
- }
- }
+ if (ds) {
+ MEMCPY(RBIGNUM(big)->as.ary, ds, BDIGIT, len);
+ xfree(ds);
+ }
+ }
+ else {
+ if (BIGNUM_LEN(big) == 0) {
+ RBIGNUM(big)->as.heap.digits = ALLOC_N(BDIGIT, len);
+ }
+ else {
+ REALLOC_N(RBIGNUM(big)->as.heap.digits, BDIGIT, len);
+ }
+ }
}
}
@@ -2985,18 +3024,19 @@ static VALUE
bignew_1(VALUE klass, size_t len, int sign)
{
NEWOBJ_OF(big, struct RBignum, klass, T_BIGNUM | (RGENGC_WB_PROTECTED_BIGNUM ? FL_WB_PROTECTED : 0));
- BIGNUM_SET_SIGN(big, sign);
+ VALUE bigv = (VALUE)big;
+ BIGNUM_SET_SIGN(bigv, sign);
if (len <= BIGNUM_EMBED_LEN_MAX) {
- RBASIC(big)->flags |= BIGNUM_EMBED_FLAG;
- BIGNUM_SET_LEN(big, len);
- (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)RBIGNUM(big)->as.ary, sizeof(RBIGNUM(big)->as.ary));
+ FL_SET_RAW(bigv, BIGNUM_EMBED_FLAG);
+ BIGNUM_SET_LEN(bigv, len);
+ (void)VALGRIND_MAKE_MEM_UNDEFINED((void*)big->as.ary, sizeof(big->as.ary));
}
else {
- RBIGNUM(big)->as.heap.digits = ALLOC_N(BDIGIT, len);
- RBIGNUM(big)->as.heap.len = len;
+ big->as.heap.digits = ALLOC_N(BDIGIT, len);
+ big->as.heap.len = len;
}
- OBJ_FREEZE(big);
- return (VALUE)big;
+ OBJ_FREEZE(bigv);
+ return bigv;
}
VALUE
@@ -3055,7 +3095,7 @@ abs2twocomp(VALUE *xp, long *n_ret)
MEMCPY(BDIGITS(z), ds, BDIGIT, n);
bary_2comp(BDIGITS(z), n);
hibits = BDIGMAX;
- *xp = z;
+ *xp = z;
}
*n_ret = n;
return hibits;
@@ -3079,7 +3119,7 @@ bigtrunc(VALUE x)
if (len == 0) return x;
while (--len && !ds[len]);
if (BIGNUM_LEN(x) > len+1) {
- rb_big_resize(x, len+1);
+ rb_big_resize(x, len+1);
}
return x;
}
@@ -3132,7 +3172,7 @@ static VALUE
bignorm(VALUE x)
{
if (RB_BIGNUM_TYPE_P(x)) {
- x = bigfixize(x);
+ x = bigfixize(x);
}
return x;
}
@@ -3154,8 +3194,8 @@ rb_uint2big(uintptr_t n)
digits[0] = n;
#else
for (i = 0; i < bdigit_roomof(SIZEOF_VALUE); i++) {
- digits[i] = BIGLO(n);
- n = BIGDN(n);
+ digits[i] = BIGLO(n);
+ n = BIGDN(n);
}
#endif
@@ -3174,14 +3214,14 @@ rb_int2big(intptr_t n)
if (n < 0) {
u = 1 + (VALUE)(-(n + 1)); /* u = -n avoiding overflow */
- neg = 1;
+ neg = 1;
}
else {
u = n;
}
big = rb_uint2big(u);
if (neg) {
- BIGNUM_SET_NEGATIVE_SIGN(big);
+ BIGNUM_SET_NEGATIVE_SIGN(big);
}
return big;
}
@@ -3340,7 +3380,7 @@ absint_numwords_generic(size_t numbytes, int nlz_bits_in_msbyte, size_t word_num
if (sign == 2) {
#if defined __GNUC__ && (__GNUC__ == 4 && __GNUC_MINOR__ == 4)
- *nlz_bits_ret = 0;
+ *nlz_bits_ret = 0;
#endif
return (size_t)-1;
}
@@ -3373,7 +3413,7 @@ rb_absint_numwords(VALUE val, size_t word_numbits, size_t *nlz_bits_ret)
size_t numbytes;
int nlz_bits_in_msbyte;
size_t numwords;
- size_t nlz_bits;
+ size_t nlz_bits = 0;
if (word_numbits == 0)
return (size_t)-1;
@@ -3388,6 +3428,7 @@ rb_absint_numwords(VALUE val, size_t word_numbits, size_t *nlz_bits_ret)
numwords0 = absint_numwords_generic(numbytes, nlz_bits_in_msbyte, word_numbits, &nlz_bits0);
assert(numwords0 == numwords);
assert(nlz_bits0 == nlz_bits);
+ (void)numwords0;
}
#endif
}
@@ -3575,7 +3616,7 @@ rb_integer_pack(VALUE val, void *words, size_t numwords, size_t wordsize, size_t
}
/*
- * Import an integer into a buffer.
+ * Import an integer from a buffer.
*
* [words] buffer to import.
* [numwords] the size of given buffer as number of words.
@@ -3659,7 +3700,7 @@ rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t na
}
else if (num_bdigits == numberof(fixbuf)) {
val = bignew((long)num_bdigits+1, 0);
- MEMCPY(BDIGITS(val), fixbuf, BDIGIT, num_bdigits);
+ MEMCPY(BDIGITS(val), fixbuf, BDIGIT, num_bdigits);
BDIGITS(val)[num_bdigits++] = 1;
}
else {
@@ -3671,11 +3712,11 @@ rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t na
BDIGIT_DBL u = fixbuf[0] + BIGUP(fixbuf[1]);
if (u == 0)
return LONG2FIX(0);
- if (0 < sign && POSFIXABLE(u))
- return LONG2FIX(u);
- if (sign < 0 && BDIGIT_MSB(fixbuf[1]) == 0 &&
+ if (0 < sign && POSFIXABLE(u))
+ return LONG2FIX((long)u);
+ if (sign < 0 && BDIGIT_MSB(fixbuf[1]) == 0 &&
NEGFIXABLE(-(BDIGIT_DBL_SIGNED)u))
- return LONG2FIX(-(BDIGIT_DBL_SIGNED)u);
+ return LONG2FIX((long)-(BDIGIT_DBL_SIGNED)u);
val = bignew((long)num_bdigits, 0 <= sign);
MEMCPY(BDIGITS(val), fixbuf, BDIGIT, num_bdigits);
}
@@ -3725,42 +3766,41 @@ str2big_scan_digits(const char *s, const char *str, int base, int badcheck, size
int c;
if (!len) {
- *num_digits_p = 0;
- *len_p = 0;
- return TRUE;
+ *num_digits_p = 0;
+ *len_p = 0;
+ return TRUE;
}
- if (badcheck && *str == '_') goto bad;
+ if (badcheck && *str == '_') return FALSE;
while ((c = *str++) != 0) {
- if (c == '_') {
- if (nondigit) {
- if (badcheck) goto bad;
- break;
- }
- nondigit = (char) c;
- }
- else if ((c = conv_digit(c)) < 0 || c >= base) {
- break;
- }
- else {
- nondigit = 0;
- num_digits++;
- digits_end = str;
- }
- if (len > 0 && !--len) break;
- }
- if (badcheck && nondigit) goto bad;
+ if (c == '_') {
+ if (nondigit) {
+ if (badcheck) return FALSE;
+ break;
+ }
+ nondigit = (char) c;
+ }
+ else if ((c = conv_digit(c)) < 0 || c >= base) {
+ break;
+ }
+ else {
+ nondigit = 0;
+ num_digits++;
+ digits_end = str;
+ }
+ if (len > 0 && !--len) break;
+ }
+ if (badcheck && nondigit) return FALSE;
if (badcheck && len) {
- str--;
- while (*str && ISSPACE(*str)) {
- str++;
- if (len > 0 && !--len) break;
- }
- if (len && *str) {
- bad:
- return FALSE;
- }
+ str--;
+ while (*str && ISSPACE(*str)) {
+ str++;
+ if (len > 0 && !--len) break;
+ }
+ if (len && *str) {
+ return FALSE;
+ }
}
*num_digits_p = num_digits;
*len_p = digits_end - digits_start;
@@ -3935,7 +3975,7 @@ str2big_karatsuba(
return z;
}
-#ifdef USE_GMP
+#if USE_GMP
static VALUE
str2big_gmp(
int sign,
@@ -3945,7 +3985,6 @@ str2big_gmp(
size_t num_bdigits,
int base)
{
- const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT;
char *buf, *p;
const char *q;
VALUE tmps;
@@ -3968,7 +4007,7 @@ str2big_gmp(
zn = num_bdigits;
z = bignew(zn, sign);
zds = BDIGITS(z);
- mpz_export(BDIGITS(z), &count, -1, sizeof(BDIGIT), 0, nails, mz);
+ bdigits_from_mpz(mz, BDIGITS(z), &count);
BDIGITS_ZERO(zds+count, zn-count);
mpz_clear(mz);
@@ -3979,6 +4018,8 @@ str2big_gmp(
}
#endif
+static VALUE rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base);
+
/*
* Parse +str+ as Ruby Integer, i.e., underscores, 0d and 0b prefixes.
*
@@ -4001,8 +4042,8 @@ rb_cstr_to_inum(const char *str, int base, int badcheck)
char *end;
VALUE ret = rb_cstr_parse_inum(str, -1, (badcheck ? NULL : &end), base);
if (NIL_P(ret)) {
- if (badcheck) rb_invalid_str(str, "Integer()");
- ret = INT2FIX(0);
+ if (badcheck) rb_invalid_str(str, "Integer()");
+ ret = INT2FIX(0);
}
return ret;
}
@@ -4026,7 +4067,7 @@ rb_cstr_to_inum(const char *str, int base, int badcheck)
VALUE
rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
- int base, int flags)
+ int base, int flags)
{
const char *const s = str;
char sign = 1;
@@ -4043,85 +4084,82 @@ rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
const int badcheck = !endp;
#define ADV(n) do {\
- if (len > 0 && len <= (n)) goto bad; \
- str += (n); \
- len -= (n); \
+ if (len > 0 && len <= (n)) goto bad; \
+ str += (n); \
+ len -= (n); \
} while (0)
#define ASSERT_LEN() do {\
- assert(len != 0); \
- if (len0 >= 0) assert(s + len0 == str + len); \
+ assert(len != 0); \
+ if (len0 >= 0) assert(s + len0 == str + len); \
} while (0)
if (!str) {
- bad:
- if (endp) *endp = (char *)str;
- if (ndigits) *ndigits = num_digits;
- return z;
+ goto bad;
}
if (len && (flags & RB_INT_PARSE_SIGN)) {
- while (ISSPACE(*str)) ADV(1);
+ while (ISSPACE(*str)) ADV(1);
- if (str[0] == '+') {
- ADV(1);
- }
- else if (str[0] == '-') {
- ADV(1);
- sign = 0;
- }
- ASSERT_LEN();
+ if (str[0] == '+') {
+ ADV(1);
+ }
+ else if (str[0] == '-') {
+ ADV(1);
+ sign = 0;
+ }
+ ASSERT_LEN();
}
if (base <= 0) {
- if (str[0] == '0' && len > 1) {
- switch (str[1]) {
- case 'x': case 'X':
- base = 16;
- ADV(2);
- break;
- case 'b': case 'B':
- base = 2;
- ADV(2);
- break;
- case 'o': case 'O':
- base = 8;
- ADV(2);
- break;
- case 'd': case 'D':
- base = 10;
- ADV(2);
- break;
- default:
- base = 8;
- }
- }
- else if (base < -1) {
- base = -base;
- }
- else {
- base = 10;
- }
+ if (str[0] == '0' && len > 1) {
+ switch (str[1]) {
+ case 'x': case 'X':
+ base = 16;
+ ADV(2);
+ break;
+ case 'b': case 'B':
+ base = 2;
+ ADV(2);
+ break;
+ case 'o': case 'O':
+ base = 8;
+ ADV(2);
+ break;
+ case 'd': case 'D':
+ base = 10;
+ ADV(2);
+ break;
+ default:
+ base = 8;
+ }
+ }
+ else if (base < -1) {
+ base = -base;
+ }
+ else {
+ base = 10;
+ }
}
else if (len == 1 || !(flags & RB_INT_PARSE_PREFIX)) {
- /* no prefix */
+ /* no prefix */
}
else if (base == 2) {
- if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
- ADV(2);
- }
+ if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
+ ADV(2);
+ }
}
else if (base == 8) {
- if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
- ADV(2);
- }
+ if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
+ ADV(2);
+ }
}
else if (base == 10) {
- if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
- ADV(2);
- }
+ if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
+ ADV(2);
+ }
}
else if (base == 16) {
- if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
- ADV(2);
- }
+ if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
+ ADV(2);
+ }
}
if (!valid_radix_p(base)) {
invalid_radix(base);
@@ -4129,80 +4167,79 @@ rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
if (!len) goto bad;
num_digits = str - s;
if (*str == '0' && len != 1) { /* squeeze preceding 0s */
- int us = 0;
- const char *end = len < 0 ? NULL : str + len;
- ++num_digits;
- while ((c = *++str) == '0' ||
- ((flags & RB_INT_PARSE_UNDERSCORE) && c == '_')) {
- if (c == '_') {
- if (++us >= 2)
- break;
- }
- else {
- ++num_digits;
- us = 0;
- }
- if (str == end) break;
- }
- if (!c || ISSPACE(c)) --str;
- if (end) len = end - str;
- ASSERT_LEN();
+ int us = 0;
+ const char *end = len < 0 ? NULL : str + len;
+ ++num_digits;
+ while ((c = *++str) == '0' ||
+ ((flags & RB_INT_PARSE_UNDERSCORE) && c == '_')) {
+ if (c == '_') {
+ if (++us >= 2)
+ break;
+ }
+ else {
+ ++num_digits;
+ us = 0;
+ }
+ if (str == end) break;
+ }
+ if (!c || ISSPACE(c)) --str;
+ if (end) len = end - str;
}
c = *str;
c = conv_digit(c);
if (c < 0 || c >= base) {
- if (!badcheck && num_digits) z = INT2FIX(0);
- goto bad;
+ if (!badcheck && num_digits) z = INT2FIX(0);
+ goto bad;
}
if (ndigits) *ndigits = num_digits;
val = ruby_scan_digits(str, len, base, &num_digits, &ov);
if (!ov) {
- const char *end = &str[num_digits];
- if (num_digits > 0 && *end == '_' && (flags & RB_INT_PARSE_UNDERSCORE))
- goto bigparse;
- if (endp) *endp = (char *)end;
- if (ndigits) *ndigits += num_digits;
- if (badcheck) {
- if (num_digits == 0) return Qnil; /* no number */
- while (len < 0 ? *end : end < str + len) {
- if (!ISSPACE(*end)) return Qnil; /* trailing garbage */
- end++;
- }
- }
-
- if (POSFIXABLE(val)) {
- if (sign) return LONG2FIX(val);
- else {
- long result = -(long)val;
- return LONG2FIX(result);
- }
- }
- else {
- VALUE big = rb_uint2big(val);
- BIGNUM_SET_SIGN(big, sign);
- return bignorm(big);
- }
+ const char *end = &str[num_digits];
+ if (num_digits > 0 && *end == '_' && (flags & RB_INT_PARSE_UNDERSCORE))
+ goto bigparse;
+ if (endp) *endp = (char *)end;
+ if (ndigits) *ndigits += num_digits;
+ if (badcheck) {
+ if (num_digits == 0) return Qnil; /* no number */
+ while (len < 0 ? *end : end < str + len) {
+ if (!ISSPACE(*end)) return Qnil; /* trailing garbage */
+ end++;
+ }
+ }
+
+ if (POSFIXABLE(val)) {
+ if (sign) return LONG2FIX(val);
+ else {
+ long result = -(long)val;
+ return LONG2FIX(result);
+ }
+ }
+ else {
+ VALUE big = rb_uint2big(val);
+ BIGNUM_SET_SIGN(big, sign);
+ return bignorm(big);
+ }
}
bigparse:
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- goto bad;
+ goto bad;
if (endp) *endp = (char *)(str + len);
if (ndigits) *ndigits += num_digits;
digits_end = digits_start + len;
if (POW2_P(base)) {
z = str2big_poweroftwo(sign, digits_start, digits_end, num_digits,
- bit_length(base-1));
+ bit_length(base-1));
}
else {
int digits_per_bdigits_dbl;
maxpow_in_bdigit_dbl(base, &digits_per_bdigits_dbl);
num_bdigits = roomof(num_digits, digits_per_bdigits_dbl)*2;
-#ifdef USE_GMP
+#if USE_GMP
if (GMP_STR2BIG_DIGITS < num_bdigits) {
z = str2big_gmp(sign, digits_start, digits_end, num_digits,
num_bdigits, base);
@@ -4220,13 +4257,18 @@ rb_int_parse_cstr(const char *str, ssize_t len, char **endp, size_t *ndigits,
}
return bignorm(z);
+
+ bad:
+ if (endp) *endp = (char *)str;
+ if (ndigits) *ndigits = num_digits;
+ return z;
}
-VALUE
+static VALUE
rb_cstr_parse_inum(const char *str, ssize_t len, char **endp, int base)
{
return rb_int_parse_cstr(str, len, endp, NULL, base,
- RB_INT_PARSE_DEFAULT);
+ RB_INT_PARSE_DEFAULT);
}
VALUE
@@ -4275,14 +4317,14 @@ rb_str2big_poweroftwo(VALUE arg, int base, int badcheck)
s = str = StringValueCStr(arg);
len = RSTRING_LEN(arg);
if (*str == '-') {
- len--;
+ len--;
str++;
positive_p = 0;
}
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- invalid_integer(arg);
+ invalid_integer(arg);
digits_end = digits_start + len;
z = str2big_poweroftwo(positive_p, digits_start, digits_end, num_digits,
@@ -4314,14 +4356,14 @@ rb_str2big_normal(VALUE arg, int base, int badcheck)
s = str = StringValuePtr(arg);
len = RSTRING_LEN(arg);
if (len > 0 && *str == '-') {
- len--;
+ len--;
str++;
positive_p = 0;
}
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- invalid_integer(arg);
+ invalid_integer(arg);
digits_end = digits_start + len;
maxpow_in_bdigit_dbl(base, &digits_per_bdigits_dbl);
@@ -4356,14 +4398,14 @@ rb_str2big_karatsuba(VALUE arg, int base, int badcheck)
s = str = StringValuePtr(arg);
len = RSTRING_LEN(arg);
if (len > 0 && *str == '-') {
- len--;
+ len--;
str++;
positive_p = 0;
}
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- invalid_integer(arg);
+ invalid_integer(arg);
digits_end = digits_start + len;
maxpow_in_bdigit_dbl(base, &digits_per_bdigits_dbl);
@@ -4377,7 +4419,7 @@ rb_str2big_karatsuba(VALUE arg, int base, int badcheck)
return bignorm(z);
}
-#ifdef USE_GMP
+#if USE_GMP
VALUE
rb_str2big_gmp(VALUE arg, int base, int badcheck)
{
@@ -4399,14 +4441,14 @@ rb_str2big_gmp(VALUE arg, int base, int badcheck)
s = str = StringValuePtr(arg);
len = RSTRING_LEN(arg);
if (len > 0 && *str == '-') {
- len--;
+ len--;
str++;
positive_p = 0;
}
digits_start = str;
if (!str2big_scan_digits(s, str, base, badcheck, &num_digits, &len))
- invalid_integer(arg);
+ invalid_integer(arg);
digits_end = digits_start + len;
maxpow_in_bdigit_dbl(base, &digits_per_bdigits_dbl);
@@ -4433,8 +4475,8 @@ rb_ull2big(unsigned LONG_LONG n)
digits[0] = n;
#else
for (i = 0; i < bdigit_roomof(SIZEOF_LONG_LONG); i++) {
- digits[i] = BIGLO(n);
- n = BIGDN(n);
+ digits[i] = BIGLO(n);
+ n = BIGDN(n);
}
#endif
@@ -4453,14 +4495,14 @@ rb_ll2big(LONG_LONG n)
if (n < 0) {
u = 1 + (unsigned LONG_LONG)(-(n + 1)); /* u = -n avoiding overflow */
- neg = 1;
+ neg = 1;
}
else {
u = n;
}
big = rb_ull2big(u);
if (neg) {
- BIGNUM_SET_NEGATIVE_SIGN(big);
+ BIGNUM_SET_NEGATIVE_SIGN(big);
}
return big;
}
@@ -4468,14 +4510,14 @@ rb_ll2big(LONG_LONG n)
VALUE
rb_ull2inum(unsigned LONG_LONG n)
{
- if (POSFIXABLE(n)) return LONG2FIX(n);
+ if (POSFIXABLE(n)) return LONG2FIX((long)n);
return rb_ull2big(n);
}
VALUE
rb_ll2inum(LONG_LONG n)
{
- if (FIXABLE(n)) return LONG2FIX(n);
+ if (FIXABLE(n)) return LONG2FIX((long)n);
return rb_ll2big(n);
}
@@ -4490,7 +4532,7 @@ rb_uint128t2big(uint128_t n)
BDIGIT *digits = BDIGITS(big);
for (i = 0; i < bdigit_roomof(SIZEOF_INT128_T); i++) {
- digits[i] = BIGLO(RSHIFT(n ,BITSPERDIG*i));
+ digits[i] = BIGLO(RSHIFT(n ,BITSPERDIG*i));
}
i = bdigit_roomof(SIZEOF_INT128_T);
@@ -4508,14 +4550,14 @@ rb_int128t2big(int128_t n)
if (n < 0) {
u = 1 + (uint128_t)(-(n + 1)); /* u = -n avoiding overflow */
- neg = 1;
+ neg = 1;
}
else {
u = n;
}
big = rb_uint128t2big(u);
if (neg) {
- BIGNUM_SET_NEGATIVE_SIGN(big);
+ BIGNUM_SET_NEGATIVE_SIGN(big);
}
return big;
}
@@ -4544,11 +4586,14 @@ big_shift3(VALUE x, int lshift_p, size_t shift_numdigits, int shift_numbits)
if (lshift_p) {
if (LONG_MAX < shift_numdigits) {
- rb_raise(rb_eArgError, "too big number");
+ too_big:
+ rb_raise(rb_eRangeError, "shift width too big");
}
s1 = shift_numdigits;
s2 = shift_numbits;
+ if ((size_t)s1 != shift_numdigits) goto too_big;
xn = BIGNUM_LEN(x);
+ if (LONG_MAX/SIZEOF_BDIGIT <= xn+s1) goto too_big;
z = bignew(xn+s1+1, BIGNUM_SIGN(x));
zds = BDIGITS(z);
BDIGITS_ZERO(zds, s1);
@@ -4639,12 +4684,6 @@ static size_t base36_numdigits_cache[35][MAX_BASE36_POWER_TABLE_ENTRIES];
static void
power_cache_init(void)
{
- int i, j;
- for (i = 0; i < 35; ++i) {
- for (j = 0; j < MAX_BASE36_POWER_TABLE_ENTRIES; ++j) {
- base36_power_cache[i][j] = Qnil;
- }
- }
}
static inline VALUE
@@ -4667,8 +4706,8 @@ power_cache_get_power(int base, int power_level, size_t *numdigits_ret)
if (MAX_BASE36_POWER_TABLE_ENTRIES <= power_level)
rb_bug("too big power number requested: maxpow_in_bdigit_dbl(%d)**(2**%d)", base, power_level);
- if (NIL_P(base36_power_cache[base - 2][power_level])) {
- VALUE power;
+ VALUE power = base36_power_cache[base - 2][power_level];
+ if (!power) {
size_t numdigits;
if (power_level == 0) {
int numdigits0;
@@ -4684,11 +4723,11 @@ power_cache_get_power(int base, int power_level, size_t *numdigits_ret)
rb_obj_hide(power);
base36_power_cache[base - 2][power_level] = power;
base36_numdigits_cache[base - 2][power_level] = numdigits;
- rb_gc_register_mark_object(power);
+ rb_gc_register_mark_object(power);
}
if (numdigits_ret)
*numdigits_ret = base36_numdigits_cache[base - 2][power_level];
- return base36_power_cache[base - 2][power_level];
+ return power;
}
struct big2str_struct {
@@ -4735,7 +4774,7 @@ big2str_2bdigits(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t tail
} while (num);
len = sizeof(buf) - j;
big2str_alloc(b2s, len + taillen);
- MEMCPY(b2s->ptr, buf + j, char, len);
+ MEMCPY(b2s->ptr, buf + j, char, len);
}
else {
p = b2s->ptr;
@@ -4752,7 +4791,7 @@ big2str_2bdigits(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t tail
static void
big2str_karatsuba(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t wn,
- int power_level, size_t taillen)
+ int power_level, size_t taillen)
{
VALUE b;
size_t half_numdigits, lower_numdigits;
@@ -4782,17 +4821,17 @@ big2str_karatsuba(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t wn,
*/
if (xn == 0 || bary_zero_p(xds, xn)) {
- if (b2s->ptr) {
+ if (b2s->ptr) {
/* When x is zero, power_cache_get_power(base, power_level) should be cached already. */
power_cache_get_power(b2s->base, power_level, &len);
- memset(b2s->ptr, '0', len);
+ memset(b2s->ptr, '0', len);
b2s->ptr += len;
- }
+ }
return;
}
if (power_level == 0) {
- big2str_2bdigits(b2s, xds, xn, taillen);
+ big2str_2bdigits(b2s, xds, xn, taillen);
return;
}
@@ -4820,7 +4859,7 @@ big2str_karatsuba(struct big2str_struct *b2s, BDIGIT *xds, size_t xn, size_t wn,
memset(b2s->ptr, '0', len);
b2s->ptr += len;
}
- big2str_2bdigits(b2s, xds, xn, taillen);
+ big2str_2bdigits(b2s, xds, xn, taillen);
}
else {
BDIGIT *qds, *rds;
@@ -4924,11 +4963,11 @@ big2str_generic(VALUE x, int base)
BARY_TRUNC(xds, xn);
if (xn == 0) {
- return rb_usascii_str_new2("0");
+ return rb_usascii_str_new2("0");
}
if (!valid_radix_p(base))
- invalid_radix(base);
+ invalid_radix(base);
if (xn >= LONG_MAX/BITSPERDIG) {
rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
@@ -4965,7 +5004,7 @@ big2str_generic(VALUE x, int base)
b2s_data.ptr = NULL;
if (power_level == 0) {
- big2str_2bdigits(&b2s_data, xds, xn, 0);
+ big2str_2bdigits(&b2s_data, xds, xn, 0);
}
else {
VALUE tmpw = 0;
@@ -4974,7 +5013,7 @@ big2str_generic(VALUE x, int base)
wn = power_level * BIGDIVREM_EXTRA_WORDS + BIGNUM_LEN(power);
wds = ALLOCV_N(BDIGIT, tmpw, xn + wn);
MEMCPY(wds, xds, BDIGIT, xn);
- big2str_karatsuba(&b2s_data, wds, xn, wn, power_level, 0);
+ big2str_karatsuba(&b2s_data, wds, xn, wn, power_level, 0);
if (tmpw)
ALLOCV_END(tmpw);
}
@@ -4993,11 +5032,10 @@ rb_big2str_generic(VALUE x, int base)
return big2str_generic(x, base);
}
-#ifdef USE_GMP
+#if USE_GMP
static VALUE
big2str_gmp(VALUE x, int base)
{
- const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT;
mpz_t mx;
size_t size;
VALUE str;
@@ -5005,7 +5043,7 @@ big2str_gmp(VALUE x, int base)
size_t xn = BIGNUM_LEN(x);
mpz_init(mx);
- mpz_import(mx, xn, -1, sizeof(BDIGIT), 0, nails, xds);
+ bdigits_to_mpz(mx, xds, xn);
size = mpz_sizeinbase(mx, base);
@@ -5041,7 +5079,7 @@ rb_big2str1(VALUE x, int base)
size_t xn;
if (FIXNUM_P(x)) {
- return rb_fix2str(x, base);
+ return rb_fix2str(x, base);
}
bigtrunc(x);
@@ -5050,11 +5088,11 @@ rb_big2str1(VALUE x, int base)
BARY_TRUNC(xds, xn);
if (xn == 0) {
- return rb_usascii_str_new2("0");
+ return rb_usascii_str_new2("0");
}
if (!valid_radix_p(base))
- invalid_radix(base);
+ invalid_radix(base);
if (xn >= LONG_MAX/BITSPERDIG) {
rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
@@ -5065,7 +5103,7 @@ rb_big2str1(VALUE x, int base)
return big2str_base_poweroftwo(x, base);
}
-#ifdef USE_GMP
+#if USE_GMP
if (GMP_BIG2STR_DIGITS < xn) {
return big2str_gmp(x, base);
}
@@ -5083,6 +5121,9 @@ rb_big2str(VALUE x, int base)
static unsigned long
big2ulong(VALUE x, const char *type)
{
+#if SIZEOF_LONG > SIZEOF_BDIGIT
+ size_t i;
+#endif
size_t len = BIGNUM_LEN(x);
unsigned long num;
BDIGIT *ds;
@@ -5097,9 +5138,9 @@ big2ulong(VALUE x, const char *type)
num = (unsigned long)ds[0];
#else
num = 0;
- while (len--) {
- num <<= BITSPERDIG;
- num += (unsigned long)ds[len]; /* overflow is already checked */
+ for (i = 0; i < len; i++) {
+ num <<= BITSPERDIG;
+ num += (unsigned long)ds[len - i - 1]; /* overflow is already checked */
}
#endif
return num;
@@ -5141,6 +5182,9 @@ rb_big2long(VALUE x)
static unsigned LONG_LONG
big2ull(VALUE x, const char *type)
{
+#if SIZEOF_LONG_LONG > SIZEOF_BDIGIT
+ size_t i;
+#endif
size_t len = BIGNUM_LEN(x);
unsigned LONG_LONG num;
BDIGIT *ds = BDIGITS(x);
@@ -5148,14 +5192,14 @@ big2ull(VALUE x, const char *type)
if (len == 0)
return 0;
if (BIGSIZE(x) > SIZEOF_LONG_LONG)
- rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
+ rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
#if SIZEOF_LONG_LONG <= SIZEOF_BDIGIT
num = (unsigned LONG_LONG)ds[0];
#else
num = 0;
- while (len--) {
- num = BIGUP(num);
- num += ds[len];
+ for (i = 0; i < len; i++) {
+ num = BIGUP(num);
+ num += ds[len - i - 1];
}
#endif
return num;
@@ -5204,23 +5248,23 @@ dbl2big(double d)
double u = (d < 0)?-d:d;
if (isinf(d)) {
- rb_raise(rb_eFloatDomainError, d < 0 ? "-Infinity" : "Infinity");
+ rb_raise(rb_eFloatDomainError, d < 0 ? "-Infinity" : "Infinity");
}
if (isnan(d)) {
- rb_raise(rb_eFloatDomainError, "NaN");
+ rb_raise(rb_eFloatDomainError, "NaN");
}
while (1.0 <= u) {
- u /= (double)(BIGRAD);
- i++;
+ u /= (double)(BIGRAD);
+ i++;
}
z = bignew(i, d>=0);
digits = BDIGITS(z);
while (i--) {
- u *= BIGRAD;
- c = (BDIGIT)u;
- u -= c;
- digits[i] = c;
+ u *= BIGRAD;
+ c = (BDIGIT)u;
+ u -= c;
+ digits[i] = c;
}
return z;
@@ -5240,43 +5284,48 @@ big2dbl(VALUE x)
BDIGIT *ds = BDIGITS(x), dl;
if (i) {
- bits = i * BITSPERDIG - nlz(ds[i-1]);
- if (bits > DBL_MANT_DIG+DBL_MAX_EXP) {
- d = HUGE_VAL;
- }
- else {
- if (bits > DBL_MANT_DIG+1)
- lo = (bits -= DBL_MANT_DIG+1) / BITSPERDIG;
- else
- bits = 0;
- while (--i > lo) {
- d = ds[i] + BIGRAD*d;
- }
- dl = ds[i];
- if (bits && (dl & ((BDIGIT)1 << (bits %= BITSPERDIG)))) {
- int carry = (dl & ~(BDIGMAX << bits)) != 0;
- if (!carry) {
- while (i-- > 0) {
- carry = ds[i] != 0;
- if (carry) break;
- }
- }
- if (carry) {
- dl &= BDIGMAX << bits;
- dl = BIGLO(dl + ((BDIGIT)1 << bits));
- if (!dl) d += 1;
- }
- }
- d = dl + BIGRAD*d;
- if (lo) {
- if (lo > INT_MAX / BITSPERDIG)
- d = HUGE_VAL;
- else if (lo < INT_MIN / BITSPERDIG)
- d = 0.0;
- else
- d = ldexp(d, (int)(lo * BITSPERDIG));
- }
- }
+ bits = i * BITSPERDIG - nlz(ds[i-1]);
+ if (bits > DBL_MANT_DIG+DBL_MAX_EXP) {
+ d = HUGE_VAL;
+ }
+ else {
+ if (bits > DBL_MANT_DIG+1)
+ lo = (bits -= DBL_MANT_DIG+1) / BITSPERDIG;
+ else
+ bits = 0;
+ while (--i > lo) {
+ d = ds[i] + BIGRAD*d;
+ }
+ dl = ds[i];
+ if (bits && (dl & ((BDIGIT)1 << (bits %= BITSPERDIG)))) {
+ int carry = (dl & ~(BDIGMAX << bits)) != 0;
+ if (!carry) {
+ while (i-- > 0) {
+ carry = ds[i] != 0;
+ if (carry) break;
+ }
+ }
+ if (carry) {
+ BDIGIT mask = BDIGMAX;
+ BDIGIT bit = 1;
+ mask <<= bits;
+ bit <<= bits;
+ dl &= mask;
+ dl += bit;
+ dl = BIGLO(dl);
+ if (!dl) d += 1;
+ }
+ }
+ d = dl + BIGRAD*d;
+ if (lo) {
+ if (lo > INT_MAX / BITSPERDIG)
+ d = HUGE_VAL;
+ else if (lo < INT_MIN / BITSPERDIG)
+ d = 0.0;
+ else
+ d = ldexp(d, (int)(lo * BITSPERDIG));
+ }
+ }
}
if (BIGNUM_NEGATIVE_P(x)) d = -d;
return d;
@@ -5288,11 +5337,11 @@ rb_big2dbl(VALUE x)
double d = big2dbl(x);
if (isinf(d)) {
- rb_warning("Bignum out of Float range");
- if (d < 0.0)
- d = -HUGE_VAL;
- else
- d = HUGE_VAL;
+ rb_warning("Integer out of Float range");
+ if (d < 0.0)
+ d = -HUGE_VAL;
+ else
+ d = HUGE_VAL;
}
return d;
}
@@ -5347,13 +5396,22 @@ rb_integer_float_cmp(VALUE x, VALUE y)
return INT2FIX(-1);
}
+#if SIZEOF_LONG * CHAR_BIT >= DBL_MANT_DIG /* assume FLT_RADIX == 2 */
+COMPILER_WARNING_PUSH
+#if __has_warning("-Wimplicit-int-float-conversion")
+COMPILER_WARNING_IGNORED(-Wimplicit-int-float-conversion)
+#endif
+static const double LONG_MAX_as_double = LONG_MAX;
+COMPILER_WARNING_POP
+#endif
+
VALUE
rb_integer_float_eq(VALUE x, VALUE y)
{
double yd = RFLOAT_VALUE(y);
double yi, yf;
- if (isnan(yd) || isinf(yd))
+ if (!isfinite(yd))
return Qfalse;
yf = modf(yd, &yi);
if (yf != 0)
@@ -5361,48 +5419,45 @@ rb_integer_float_eq(VALUE x, VALUE y)
if (FIXNUM_P(x)) {
#if SIZEOF_LONG * CHAR_BIT < DBL_MANT_DIG /* assume FLT_RADIX == 2 */
double xd = (double)FIX2LONG(x);
- if (xd != yd)
- return Qfalse;
- return Qtrue;
+ return RBOOL(xd == yd);
#else
long xn, yn;
- if (yi < LONG_MIN || LONG_MAX < yi)
+ if (yi < LONG_MIN || LONG_MAX_as_double <= yi)
return Qfalse;
xn = FIX2LONG(x);
yn = (long)yi;
- if (xn != yn)
- return Qfalse;
- return Qtrue;
+ return RBOOL(xn == yn);
#endif
}
y = rb_dbl2big(yi);
return rb_big_eq(x, y);
}
+
VALUE
rb_big_cmp(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- x = bigfixize(x);
+ x = bigfixize(x);
if (FIXNUM_P(x)) {
- /* SIGNED_VALUE and Fixnum have same sign-bits, same
- * order */
- SIGNED_VALUE sx = (SIGNED_VALUE)x, sy = (SIGNED_VALUE)y;
- if (sx < sy) return INT2FIX(-1);
- return INT2FIX(sx > sy);
+ /* SIGNED_VALUE and Fixnum have same sign-bits, same
+ * order */
+ SIGNED_VALUE sx = (SIGNED_VALUE)x, sy = (SIGNED_VALUE)y;
+ if (sx < sy) return INT2FIX(-1);
+ return INT2FIX(sx > sy);
}
}
else if (RB_BIGNUM_TYPE_P(y)) {
- if (BIGNUM_SIGN(x) == BIGNUM_SIGN(y)) {
- int cmp = bary_cmp(BDIGITS(x), BIGNUM_LEN(x), BDIGITS(y), BIGNUM_LEN(y));
- return INT2FIX(BIGNUM_SIGN(x) ? cmp : -cmp);
- }
+ if (BIGNUM_SIGN(x) == BIGNUM_SIGN(y)) {
+ int cmp = bary_cmp(BDIGITS(x), BIGNUM_LEN(x), BDIGITS(y), BIGNUM_LEN(y));
+ return INT2FIX(BIGNUM_SIGN(x) ? cmp : -cmp);
+ }
}
else if (RB_FLOAT_TYPE_P(y)) {
return rb_integer_float_cmp(x, y);
}
else {
- return rb_num_coerce_cmp(x, y, idCmp);
+ return rb_num_coerce_cmp(x, y, idCmp);
}
return INT2FIX(BIGNUM_SIGN(x) ? 1 : -1);
}
@@ -5421,30 +5476,30 @@ big_op(VALUE x, VALUE y, enum big_op_t op)
int n;
if (RB_INTEGER_TYPE_P(y)) {
- rel = rb_big_cmp(x, y);
+ rel = rb_big_cmp(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
rel = rb_integer_float_cmp(x, y);
}
else {
- ID id = 0;
- switch (op) {
- case big_op_gt: id = '>'; break;
- case big_op_ge: id = idGE; break;
- case big_op_lt: id = '<'; break;
- case big_op_le: id = idLE; break;
- }
- return rb_num_coerce_relop(x, y, id);
+ ID id = 0;
+ switch (op) {
+ case big_op_gt: id = '>'; break;
+ case big_op_ge: id = idGE; break;
+ case big_op_lt: id = '<'; break;
+ case big_op_le: id = idLE; break;
+ }
+ return rb_num_coerce_relop(x, y, id);
}
if (NIL_P(rel)) return Qfalse;
n = FIX2INT(rel);
switch (op) {
- case big_op_gt: return n > 0 ? Qtrue : Qfalse;
- case big_op_ge: return n >= 0 ? Qtrue : Qfalse;
- case big_op_lt: return n < 0 ? Qtrue : Qfalse;
- case big_op_le: return n <= 0 ? Qtrue : Qfalse;
+ case big_op_gt: return RBOOL(n > 0);
+ case big_op_ge: return RBOOL(n >= 0);
+ case big_op_lt: return RBOOL(n < 0);
+ case big_op_le: return RBOOL(n <= 0);
}
return Qundef;
}
@@ -5478,8 +5533,8 @@ rb_big_le(VALUE x, VALUE y)
* big == obj -> true or false
*
* Returns <code>true</code> only if <i>obj</i> has the same value
- * as <i>big</i>. Contrast this with <code>Integer#eql?</code>, which
- * requires <i>obj</i> to be a <code>Integer</code>.
+ * as <i>big</i>. Contrast this with Integer#eql?, which requires
+ * <i>obj</i> to be an Integer.
*
* 68719476736 == 68719476736.0 #=> true
*/
@@ -5488,7 +5543,7 @@ VALUE
rb_big_eq(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- return bignorm(x) == y ? Qtrue : Qfalse;
+ return RBOOL(bignorm(x) == y);
}
else if (RB_BIGNUM_TYPE_P(y)) {
}
@@ -5496,12 +5551,11 @@ rb_big_eq(VALUE x, VALUE y)
return rb_integer_float_eq(x, y);
}
else {
- return rb_equal(y, x);
+ return rb_equal(y, x);
}
if (BIGNUM_SIGN(x) != BIGNUM_SIGN(y)) return Qfalse;
if (BIGNUM_LEN(x) != BIGNUM_LEN(y)) return Qfalse;
- if (MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,BIGNUM_LEN(y)) != 0) return Qfalse;
- return Qtrue;
+ return RBOOL(MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,BIGNUM_LEN(y)) == 0);
}
VALUE
@@ -5510,8 +5564,7 @@ rb_big_eql(VALUE x, VALUE y)
if (!RB_BIGNUM_TYPE_P(y)) return Qfalse;
if (BIGNUM_SIGN(x) != BIGNUM_SIGN(y)) return Qfalse;
if (BIGNUM_LEN(x) != BIGNUM_LEN(y)) return Qfalse;
- if (MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,BIGNUM_LEN(y)) != 0) return Qfalse;
- return Qtrue;
+ return RBOOL(MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,BIGNUM_LEN(y)) == 0);
}
VALUE
@@ -5605,10 +5658,10 @@ bigsub_int(VALUE x, long y0)
assert(xn == zn);
num = (BDIGIT_DBL_SIGNED)xds[0] - y;
if (xn == 1 && num < 0) {
- BIGNUM_NEGATE(z);
- zds[0] = (BDIGIT)-num;
- RB_GC_GUARD(x);
- return bignorm(z);
+ BIGNUM_NEGATE(z);
+ zds[0] = (BDIGIT)-num;
+ RB_GC_GUARD(x);
+ return bignorm(z);
}
zds[0] = BIGLO(num);
num = BIGDN(num);
@@ -5620,10 +5673,10 @@ bigsub_int(VALUE x, long y0)
num = 0;
for (i=0; i < xn; i++) {
if (y == 0) goto y_is_zero_x;
- num += (BDIGIT_DBL_SIGNED)xds[i] - BIGLO(y);
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- y = BIGDN(y);
+ num += (BDIGIT_DBL_SIGNED)xds[i] - BIGLO(y);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
+ y = BIGDN(y);
}
for (; i < zn; i++) {
if (y == 0) goto y_is_zero_z;
@@ -5638,9 +5691,9 @@ bigsub_int(VALUE x, long y0)
for (; i < xn; i++) {
y_is_zero_x:
if (num == 0) goto num_is_zero_x;
- num += xds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += xds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
#if SIZEOF_BDIGIT < SIZEOF_LONG
for (; i < zn; i++) {
@@ -5654,7 +5707,7 @@ bigsub_int(VALUE x, long y0)
for (; i < xn; i++) {
num_is_zero_x:
- zds[i] = xds[i];
+ zds[i] = xds[i];
}
#if SIZEOF_BDIGIT < SIZEOF_LONG
for (; i < zn; i++) {
@@ -5668,7 +5721,7 @@ bigsub_int(VALUE x, long y0)
assert(num == 0 || num == -1);
if (num < 0) {
get2comp(z);
- BIGNUM_NEGATE(z);
+ BIGNUM_NEGATE(z);
}
RB_GC_GUARD(x);
return bignorm(z);
@@ -5711,17 +5764,17 @@ bigadd_int(VALUE x, long y)
num = 0;
for (i=0; i < xn; i++) {
if (y == 0) goto y_is_zero_x;
- num += (BDIGIT_DBL)xds[i] + BIGLO(y);
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- y = BIGDN(y);
+ num += (BDIGIT_DBL)xds[i] + BIGLO(y);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
+ y = BIGDN(y);
}
for (; i < zn; i++) {
if (y == 0) goto y_is_zero_z;
- num += BIGLO(y);
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- y = BIGDN(y);
+ num += BIGLO(y);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
+ y = BIGDN(y);
}
goto finish;
@@ -5730,25 +5783,25 @@ bigadd_int(VALUE x, long y)
for (;i < xn; i++) {
y_is_zero_x:
if (num == 0) goto num_is_zero_x;
- num += (BDIGIT_DBL)xds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ num += (BDIGIT_DBL)xds[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
for (; i < zn; i++) {
y_is_zero_z:
if (num == 0) goto num_is_zero_z;
- zds[i] = BIGLO(num);
- num = BIGDN(num);
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
}
goto finish;
for (;i < xn; i++) {
num_is_zero_x:
- zds[i] = xds[i];
+ zds[i] = xds[i];
}
for (; i < zn; i++) {
num_is_zero_z:
- zds[i] = 0;
+ zds[i] = 0;
}
goto finish;
@@ -5765,15 +5818,15 @@ bigadd(VALUE x, VALUE y, int sign)
sign = (sign == BIGNUM_SIGN(y));
if (BIGNUM_SIGN(x) != sign) {
- if (sign) return bigsub(y, x);
- return bigsub(x, y);
+ if (sign) return bigsub(y, x);
+ return bigsub(x, y);
}
if (BIGNUM_LEN(x) > BIGNUM_LEN(y)) {
- len = BIGNUM_LEN(x) + 1;
+ len = BIGNUM_LEN(x) + 1;
}
else {
- len = BIGNUM_LEN(y) + 1;
+ len = BIGNUM_LEN(y) + 1;
}
z = bignew(len, sign);
@@ -5790,26 +5843,26 @@ rb_big_plus(VALUE x, VALUE y)
long n;
if (FIXNUM_P(y)) {
- n = FIX2LONG(y);
- if ((n > 0) != BIGNUM_SIGN(x)) {
- if (n < 0) {
- n = -n;
- }
- return bigsub_int(x, n);
- }
- if (n < 0) {
- n = -n;
- }
- return bigadd_int(x, n);
+ n = FIX2LONG(y);
+ if ((n > 0) != BIGNUM_SIGN(x)) {
+ if (n < 0) {
+ n = -n;
+ }
+ return bigsub_int(x, n);
+ }
+ if (n < 0) {
+ n = -n;
+ }
+ return bigadd_int(x, n);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return bignorm(bigadd(x, y, 1));
+ return bignorm(bigadd(x, y, 1));
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(rb_big2dbl(x) + RFLOAT_VALUE(y));
+ return DBL2NUM(rb_big2dbl(x) + RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '+');
+ return rb_num_coerce_bin(x, y, '+');
}
}
@@ -5819,26 +5872,26 @@ rb_big_minus(VALUE x, VALUE y)
long n;
if (FIXNUM_P(y)) {
- n = FIX2LONG(y);
- if ((n > 0) != BIGNUM_SIGN(x)) {
- if (n < 0) {
- n = -n;
- }
- return bigadd_int(x, n);
- }
- if (n < 0) {
- n = -n;
- }
- return bigsub_int(x, n);
+ n = FIX2LONG(y);
+ if ((n > 0) != BIGNUM_SIGN(x)) {
+ if (n < 0) {
+ n = -n;
+ }
+ return bigadd_int(x, n);
+ }
+ if (n < 0) {
+ n = -n;
+ }
+ return bigsub_int(x, n);
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return bignorm(bigadd(x, y, 0));
+ return bignorm(bigadd(x, y, 0));
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(rb_big2dbl(x) - RFLOAT_VALUE(y));
+ return DBL2NUM(rb_big2dbl(x) - RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '-');
+ return rb_num_coerce_bin(x, y, '-');
}
}
@@ -5897,15 +5950,15 @@ VALUE
rb_big_mul(VALUE x, VALUE y)
{
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
}
else if (RB_FLOAT_TYPE_P(y)) {
- return DBL2NUM(rb_big2dbl(x) * RFLOAT_VALUE(y));
+ return DBL2NUM(rb_big2dbl(x) * RFLOAT_VALUE(y));
}
else {
- return rb_num_coerce_bin(x, y, '*');
+ return rb_num_coerce_bin(x, y, '*');
}
return bignorm(bigmul0(x, y));
@@ -5932,21 +5985,21 @@ bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp)
BARY_TRUNC(xds, xn);
if (xn < yn || (xn == yn && xds[xn - 1] < yds[yn - 1])) {
- if (divp) *divp = rb_int2big(0);
- if (modp) *modp = x;
- return Qnil;
+ if (divp) *divp = rb_int2big(0);
+ if (modp) *modp = x;
+ return Qnil;
}
if (yn == 1) {
- dd = yds[0];
- z = bignew(xn, BIGNUM_SIGN(x)==BIGNUM_SIGN(y));
- zds = BDIGITS(z);
+ dd = yds[0];
+ z = bignew(xn, BIGNUM_SIGN(x)==BIGNUM_SIGN(y));
+ zds = BDIGITS(z);
dd = bigdivrem_single(zds, xds, xn, dd);
- if (modp) {
- *modp = rb_uint2big((uintptr_t)dd);
- BIGNUM_SET_SIGN(*modp, BIGNUM_SIGN(x));
- }
- if (divp) *divp = z;
- return Qnil;
+ if (modp) {
+ *modp = rb_uint2big((uintptr_t)dd);
+ BIGNUM_SET_SIGN(*modp, BIGNUM_SIGN(x));
+ }
+ if (divp) *divp = z;
+ return Qnil;
}
if (xn == 2 && yn == 2) {
BDIGIT_DBL x0 = bary2bdigitdbl(xds, 2);
@@ -6011,11 +6064,11 @@ bigdivmod(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp)
bigdivrem(x, y, divp, &mod);
if (BIGNUM_SIGN(x) != BIGNUM_SIGN(y) && !BIGZEROP(mod)) {
- if (divp) *divp = bigadd(*divp, rb_int2big(1), 0);
- if (modp) *modp = bigadd(mod, y, 1);
+ if (divp) *divp = bigadd(*divp, rb_int2big(1), 0);
+ if (modp) *modp = bigadd(mod, y, 1);
}
else if (modp) {
- *modp = mod;
+ *modp = mod;
}
}
@@ -6026,22 +6079,25 @@ rb_big_divide(VALUE x, VALUE y, ID op)
VALUE z;
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (RB_BIGNUM_TYPE_P(y)) {
}
else if (RB_FLOAT_TYPE_P(y)) {
- if (op == '/') {
- return DBL2NUM(rb_big2dbl(x) / RFLOAT_VALUE(y));
- }
- else {
- double dy = RFLOAT_VALUE(y);
- if (dy == 0.0) rb_num_zerodiv();
- return rb_dbl2big(rb_big2dbl(x) / dy);
- }
+ if (op == '/') {
+ double dx = rb_big2dbl(x);
+ return rb_flo_div_flo(DBL2NUM(dx), y);
+ }
+ else {
+ VALUE v;
+ double dy = RFLOAT_VALUE(y);
+ if (dy == 0.0) rb_num_zerodiv();
+ v = rb_big_divide(x, y, '/');
+ return rb_dbl2big(RFLOAT_VALUE(v));
+ }
}
else {
- return rb_num_coerce_bin(x, y, op);
+ return rb_num_coerce_bin(x, y, op);
}
bigdivmod(x, y, &z, 0);
@@ -6057,7 +6113,7 @@ rb_big_div(VALUE x, VALUE y)
VALUE
rb_big_idiv(VALUE x, VALUE y)
{
- return rb_big_divide(x, y, rb_intern("div"));
+ return rb_big_divide(x, y, idDiv);
}
VALUE
@@ -6066,10 +6122,10 @@ rb_big_modulo(VALUE x, VALUE y)
VALUE z;
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (!RB_BIGNUM_TYPE_P(y)) {
- return rb_num_coerce_bin(x, y, '%');
+ return rb_num_coerce_bin(x, y, '%');
}
bigdivmod(x, y, 0, &z);
@@ -6082,10 +6138,10 @@ rb_big_remainder(VALUE x, VALUE y)
VALUE z;
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (!RB_BIGNUM_TYPE_P(y)) {
- return rb_num_coerce_bin(x, y, rb_intern("remainder"));
+ return rb_num_coerce_bin(x, y, rb_intern("remainder"));
}
bigdivrem(x, y, 0, &z);
@@ -6098,10 +6154,10 @@ rb_big_divmod(VALUE x, VALUE y)
VALUE div, mod;
if (FIXNUM_P(y)) {
- y = rb_int2big(FIX2LONG(y));
+ y = rb_int2big(FIX2LONG(y));
}
else if (!RB_BIGNUM_TYPE_P(y)) {
- return rb_num_coerce_bin(x, y, rb_intern("divmod"));
+ return rb_num_coerce_bin(x, y, idDivmod);
}
bigdivmod(x, y, &div, &mod);
@@ -6112,9 +6168,9 @@ static VALUE
big_shift(VALUE x, long n)
{
if (n < 0)
- return big_lshift(x, 1+(unsigned long)(-(n+1)));
+ return big_lshift(x, 1+(unsigned long)(-(n+1)));
else if (n > 0)
- return big_rshift(x, (unsigned long)n);
+ return big_rshift(x, (unsigned long)n);
return x;
}
@@ -6138,9 +6194,9 @@ big_fdiv(VALUE x, VALUE y, long ey)
l = ex - ey;
#if SIZEOF_LONG > SIZEOF_INT
{
- /* Visual C++ can't be here */
- if (l > INT_MAX) return HUGE_VAL;
- if (l < INT_MIN) return 0.0;
+ /* Visual C++ can't be here */
+ if (l > INT_MAX) return HUGE_VAL;
+ if (l < INT_MIN) return 0.0;
}
#endif
return ldexp(big2dbl(z), (int)l);
@@ -6170,27 +6226,29 @@ double
rb_big_fdiv_double(VALUE x, VALUE y)
{
double dx, dy;
+ VALUE v;
dx = big2dbl(x);
if (FIXNUM_P(y)) {
- dy = (double)FIX2LONG(y);
- if (isinf(dx))
- return big_fdiv_int(x, rb_int2big(FIX2LONG(y)));
+ dy = (double)FIX2LONG(y);
+ if (isinf(dx))
+ return big_fdiv_int(x, rb_int2big(FIX2LONG(y)));
}
else if (RB_BIGNUM_TYPE_P(y)) {
- return big_fdiv_int(x, y);
+ return big_fdiv_int(x, y);
}
else if (RB_FLOAT_TYPE_P(y)) {
- dy = RFLOAT_VALUE(y);
- if (isnan(dy))
- return dy;
- if (isinf(dx))
- return big_fdiv_float(x, y);
+ dy = RFLOAT_VALUE(y);
+ if (isnan(dy))
+ return dy;
+ if (isinf(dx))
+ return big_fdiv_float(x, y);
}
else {
- return NUM2DBL(rb_num_coerce_bin(x, y, rb_intern("fdiv")));
+ return NUM2DBL(rb_num_coerce_bin(x, y, idFdiv));
}
- return dx / dy;
+ v = rb_flo_div_flo(DBL2NUM(dx), DBL2NUM(dy));
+ return NUM2DBL(v);
}
VALUE
@@ -6207,49 +6265,55 @@ rb_big_pow(VALUE x, VALUE y)
again:
if (y == INT2FIX(0)) return INT2FIX(1);
+ if (y == INT2FIX(1)) return x;
if (RB_FLOAT_TYPE_P(y)) {
- d = RFLOAT_VALUE(y);
- if ((BIGNUM_NEGATIVE_P(x) && !BIGZEROP(x))) {
- return rb_dbl_complex_polar(pow(-rb_big2dbl(x), d), d);
- }
+ d = RFLOAT_VALUE(y);
+ if ((BIGNUM_NEGATIVE_P(x) && !BIGZEROP(x))) {
+ return rb_dbl_complex_new_polar_pi(pow(-rb_big2dbl(x), d), d);
+ }
}
else if (RB_BIGNUM_TYPE_P(y)) {
- y = bignorm(y);
- if (FIXNUM_P(y))
- goto again;
- rb_warn("in a**b, b may be too big");
- d = rb_big2dbl(y);
+ y = bignorm(y);
+ if (FIXNUM_P(y))
+ goto again;
+ rb_warn("in a**b, b may be too big");
+ d = rb_big2dbl(y);
}
else if (FIXNUM_P(y)) {
- yy = FIX2LONG(y);
+ yy = FIX2LONG(y);
- if (yy < 0)
- return rb_rational_raw(INT2FIX(1), rb_big_pow(x, INT2NUM(-yy)));
- else {
- VALUE z = 0;
- SIGNED_VALUE mask;
+ if (yy < 0) {
+ x = rb_big_pow(x, LONG2NUM(-yy));
+ if (RB_INTEGER_TYPE_P(x))
+ return rb_rational_raw(INT2FIX(1), x);
+ else
+ return DBL2NUM(1.0 / NUM2DBL(x));
+ }
+ else {
+ VALUE z = 0;
+ SIGNED_VALUE mask;
const size_t xbits = rb_absint_numwords(x, 1, NULL);
- const size_t BIGLEN_LIMIT = 32*1024*1024;
+ const size_t BIGLEN_LIMIT = 32*1024*1024;
- if (xbits == (size_t)-1 ||
+ if (xbits == (size_t)-1 ||
(xbits > BIGLEN_LIMIT) ||
(xbits * yy > BIGLEN_LIMIT)) {
- rb_warn("in a**b, b may be too big");
- d = (double)yy;
- }
- else {
- for (mask = FIXNUM_MAX + 1; mask; mask >>= 1) {
- if (z) z = bigsq(z);
- if (yy & mask) {
- z = z ? bigtrunc(bigmul0(z, x)) : x;
- }
- }
- return bignorm(z);
- }
- }
+ rb_warn("in a**b, b may be too big");
+ d = (double)yy;
+ }
+ else {
+ for (mask = FIXNUM_MAX + 1; mask; mask >>= 1) {
+ if (z) z = bigsq(z);
+ if (yy & mask) {
+ z = z ? bigtrunc(bigmul0(z, x)) : x;
+ }
+ }
+ return bignorm(z);
+ }
+ }
}
else {
- return rb_num_coerce_bin(x, y, idPow);
+ return rb_num_coerce_bin(x, y, idPow);
}
return DBL2NUM(pow(rb_big2dbl(x), d));
}
@@ -6269,8 +6333,8 @@ bigand_int(VALUE x, long xn, BDIGIT hibitsx, long y)
xds = BDIGITS(x);
#if SIZEOF_BDIGIT >= SIZEOF_LONG
if (!hibitsy) {
- y &= xds[0];
- return LONG2NUM(y);
+ y &= xds[0];
+ return LONG2NUM(y);
}
#endif
@@ -6299,10 +6363,10 @@ bigand_int(VALUE x, long xn, BDIGIT hibitsx, long y)
}
#endif
for (;i < xn; i++) {
- zds[i] = xds[i] & hibitsy;
+ zds[i] = xds[i] & hibitsy;
}
for (;i < zn; i++) {
- zds[i] = hibitsx & hibitsy;
+ zds[i] = hibitsx & hibitsy;
}
twocomp2abs_bang(z, hibitsx && hibitsy);
RB_GC_GUARD(x);
@@ -6322,12 +6386,12 @@ rb_big_and(VALUE x, VALUE y)
long tmpn;
if (!RB_INTEGER_TYPE_P(y)) {
- return rb_num_coerce_bit(x, y, '&');
+ return rb_num_coerce_bit(x, y, '&');
}
hibitsx = abs2twocomp(&x, &xn);
if (FIXNUM_P(y)) {
- return bigand_int(x, xn, hibitsx, FIX2LONG(y));
+ return bigand_int(x, xn, hibitsx, FIX2LONG(y));
}
hibitsy = abs2twocomp(&y, &yn);
if (xn > yn) {
@@ -6349,10 +6413,10 @@ rb_big_and(VALUE x, VALUE y)
zds = BDIGITS(z);
for (i=0; i<n1; i++) {
- zds[i] = ds1[i] & ds2[i];
+ zds[i] = ds1[i] & ds2[i];
}
for (; i<n2; i++) {
- zds[i] = hibits1 & ds2[i];
+ zds[i] = hibits1 & ds2[i];
}
twocomp2abs_bang(z, hibits1 && hibits2);
RB_GC_GUARD(x);
@@ -6441,12 +6505,12 @@ rb_big_or(VALUE x, VALUE y)
long tmpn;
if (!RB_INTEGER_TYPE_P(y)) {
- return rb_num_coerce_bit(x, y, '|');
+ return rb_num_coerce_bit(x, y, '|');
}
hibitsx = abs2twocomp(&x, &xn);
if (FIXNUM_P(y)) {
- return bigor_int(x, xn, hibitsx, FIX2LONG(y));
+ return bigor_int(x, xn, hibitsx, FIX2LONG(y));
}
hibitsy = abs2twocomp(&y, &yn);
if (xn > yn) {
@@ -6468,10 +6532,10 @@ rb_big_or(VALUE x, VALUE y)
zds = BDIGITS(z);
for (i=0; i<n1; i++) {
- zds[i] = ds1[i] | ds2[i];
+ zds[i] = ds1[i] | ds2[i];
}
for (; i<n2; i++) {
- zds[i] = hibits1 | ds2[i];
+ zds[i] = hibits1 | ds2[i];
}
twocomp2abs_bang(z, hibits1 || hibits2);
RB_GC_GUARD(x);
@@ -6535,12 +6599,12 @@ rb_big_xor(VALUE x, VALUE y)
long tmpn;
if (!RB_INTEGER_TYPE_P(y)) {
- return rb_num_coerce_bit(x, y, '^');
+ return rb_num_coerce_bit(x, y, '^');
}
hibitsx = abs2twocomp(&x, &xn);
if (FIXNUM_P(y)) {
- return bigxor_int(x, xn, hibitsx, FIX2LONG(y));
+ return bigxor_int(x, xn, hibitsx, FIX2LONG(y));
}
hibitsy = abs2twocomp(&y, &yn);
if (xn > yn) {
@@ -6559,10 +6623,10 @@ rb_big_xor(VALUE x, VALUE y)
zds = BDIGITS(z);
for (i=0; i<n1; i++) {
- zds[i] = ds1[i] ^ ds2[i];
+ zds[i] = ds1[i] ^ ds2[i];
}
for (; i<n2; i++) {
- zds[i] = hibitsx ^ ds2[i];
+ zds[i] = hibitsx ^ ds2[i];
}
twocomp2abs_bang(z, (hibits1 ^ hibits2) != 0);
RB_GC_GUARD(x);
@@ -6578,25 +6642,25 @@ rb_big_lshift(VALUE x, VALUE y)
int shift_numbits;
for (;;) {
- if (FIXNUM_P(y)) {
- long l = FIX2LONG(y);
+ if (FIXNUM_P(y)) {
+ long l = FIX2LONG(y);
unsigned long shift;
- if (0 <= l) {
- lshift_p = 1;
+ if (0 <= l) {
+ lshift_p = 1;
shift = l;
}
else {
- lshift_p = 0;
- shift = 1+(unsigned long)(-(l+1));
- }
+ lshift_p = 0;
+ shift = 1+(unsigned long)(-(l+1));
+ }
shift_numbits = (int)(shift & (BITSPERDIG-1));
shift_numdigits = shift >> bit_length(BITSPERDIG-1);
return bignorm(big_shift3(x, lshift_p, shift_numdigits, shift_numbits));
- }
- else if (RB_BIGNUM_TYPE_P(y)) {
+ }
+ else if (RB_BIGNUM_TYPE_P(y)) {
return bignorm(big_shift2(x, 1, y));
- }
- y = rb_to_int(y);
+ }
+ y = rb_to_int(y);
}
}
@@ -6608,8 +6672,8 @@ rb_big_rshift(VALUE x, VALUE y)
int shift_numbits;
for (;;) {
- if (FIXNUM_P(y)) {
- long l = FIX2LONG(y);
+ if (FIXNUM_P(y)) {
+ long l = FIX2LONG(y);
unsigned long shift;
if (0 <= l) {
lshift_p = 0;
@@ -6617,16 +6681,16 @@ rb_big_rshift(VALUE x, VALUE y)
}
else {
lshift_p = 1;
- shift = 1+(unsigned long)(-(l+1));
- }
+ shift = 1+(unsigned long)(-(l+1));
+ }
shift_numbits = (int)(shift & (BITSPERDIG-1));
shift_numdigits = shift >> bit_length(BITSPERDIG-1);
return bignorm(big_shift3(x, lshift_p, shift_numdigits, shift_numbits));
- }
- else if (RB_BIGNUM_TYPE_P(y)) {
+ }
+ else if (RB_BIGNUM_TYPE_P(y)) {
return bignorm(big_shift2(x, 0, y));
- }
- y = rb_to_int(y);
+ }
+ y = rb_to_int(y);
}
}
@@ -6640,29 +6704,29 @@ rb_big_aref(VALUE x, VALUE y)
BDIGIT bit;
if (RB_BIGNUM_TYPE_P(y)) {
- if (BIGNUM_NEGATIVE_P(y))
- return INT2FIX(0);
- bigtrunc(y);
- if (BIGSIZE(y) > sizeof(size_t)) {
- out_of_range:
- return BIGNUM_SIGN(x) ? INT2FIX(0) : INT2FIX(1);
- }
+ if (BIGNUM_NEGATIVE_P(y))
+ return INT2FIX(0);
+ bigtrunc(y);
+ if (BIGSIZE(y) > sizeof(size_t)) {
+ return BIGNUM_SIGN(x) ? INT2FIX(0) : INT2FIX(1);
+ }
#if SIZEOF_SIZE_T <= SIZEOF_LONG
- shift = big2ulong(y, "long");
+ shift = big2ulong(y, "long");
#else
- shift = big2ull(y, "long long");
+ shift = big2ull(y, "long long");
#endif
}
else {
- l = NUM2LONG(y);
- if (l < 0) return INT2FIX(0);
- shift = (size_t)l;
+ l = NUM2LONG(y);
+ if (l < 0) return INT2FIX(0);
+ shift = (size_t)l;
}
s1 = shift/BITSPERDIG;
s2 = shift%BITSPERDIG;
bit = (BDIGIT)1 << s2;
- if (s1 >= BIGNUM_LEN(x)) goto out_of_range;
+ if (s1 >= BIGNUM_LEN(x))
+ return BIGNUM_SIGN(x) ? INT2FIX(0) : INT2FIX(1);
xds = BDIGITS(x);
if (BIGNUM_POSITIVE_P(x))
@@ -6686,14 +6750,15 @@ rb_big_hash(VALUE x)
/*
* call-seq:
- * big.coerce(numeric) -> array
+ * int.coerce(numeric) -> array
*
- * Returns an array with both a +numeric+ and a +big+ represented as Bignum
- * objects.
+ * Returns an array with both a +numeric+ and a +int+ represented as
+ * Integer objects or Float objects.
*
- * This is achieved by converting +numeric+ to a Bignum.
+ * This is achieved by converting +numeric+ to an Integer or a Float.
*
- * A TypeError is raised if the +numeric+ is not a Fixnum or Bignum type.
+ * A TypeError is raised if the +numeric+ is not an Integer or a Float
+ * type.
*
* (0x3FFFFFFFFFFFFFFF+1).coerce(42) #=> [42, 4611686018427387904]
*/
@@ -6715,8 +6780,8 @@ VALUE
rb_big_abs(VALUE x)
{
if (BIGNUM_NEGATIVE_P(x)) {
- x = rb_big_clone(x);
- BIGNUM_SET_POSITIVE_SIGN(x);
+ x = rb_big_clone(x);
+ BIGNUM_SET_POSITIVE_SIGN(x);
}
return x;
}
@@ -6783,17 +6848,14 @@ rb_big_bit_length(VALUE big)
VALUE
rb_big_odd_p(VALUE num)
{
- if (BIGNUM_LEN(num) != 0 && BDIGITS(num)[0] & 1) {
- return Qtrue;
- }
- return Qfalse;
+ return RBOOL(BIGNUM_LEN(num) != 0 && BDIGITS(num)[0] & 1);
}
VALUE
rb_big_even_p(VALUE num)
{
if (BIGNUM_LEN(num) != 0 && BDIGITS(num)[0] & 1) {
- return Qfalse;
+ return Qfalse;
}
return Qtrue;
}
@@ -6824,26 +6886,34 @@ estimate_initial_sqrt(VALUE *xp, const size_t xn, const BDIGIT *nds, size_t len)
double f;
if (rshift > 0) {
- lowbits = (BDIGIT)d & ~(~(BDIGIT)1U << rshift);
- d >>= rshift;
+ lowbits = (BDIGIT)d & ~(~(BDIGIT)1U << rshift);
+ d >>= rshift;
}
else if (rshift < 0) {
- d <<= -rshift;
- d |= nds[len-dbl_per_bdig-1] >> (BITSPERDIG+rshift);
+ d <<= -rshift;
+ d |= nds[len-dbl_per_bdig-1] >> (BITSPERDIG+rshift);
}
f = sqrt(BDIGIT_DBL_TO_DOUBLE(d));
d = (BDIGIT_DBL)ceil(f);
if (BDIGIT_DBL_TO_DOUBLE(d) == f) {
- if (lowbits || (lowbits = !bary_zero_p(nds, len-dbl_per_bdig)))
- ++d;
+ if (lowbits || (lowbits = !bary_zero_p(nds, len-dbl_per_bdig)))
+ ++d;
}
else {
- lowbits = 1;
+ lowbits = 1;
}
rshift /= 2;
rshift += (2-(len&1))*BITSPERDIG/2;
if (rshift >= 0) {
- d <<= rshift;
+ if (nlz((BDIGIT)d) + rshift >= BITSPERDIG) {
+ /* (d << rshift) does cause overflow.
+ * example: Integer.sqrt(0xffff_ffff_ffff_ffff ** 2)
+ */
+ d = ~(BDIGIT_DBL)0;
+ }
+ else {
+ d <<= rshift;
+ }
}
BDIGITS_ZERO(xds, xn-2);
bdigitdbl2bary(&xds[xn-2], 2, d);
@@ -6862,52 +6932,49 @@ rb_big_isqrt(VALUE n)
BDIGIT *xds;
if (len <= 2) {
- BDIGIT sq = rb_bdigit_dbl_isqrt(bary2bdigitdbl(nds, len));
+ BDIGIT sq = rb_bdigit_dbl_isqrt(bary2bdigitdbl(nds, len));
#if SIZEOF_BDIGIT > SIZEOF_LONG
- return ULL2NUM(sq);
+ return ULL2NUM(sq);
#else
- return ULONG2NUM(sq);
+ return ULONG2NUM(sq);
#endif
}
else if ((xds = estimate_initial_sqrt(&x, xn, nds, len)) != 0) {
- size_t tn = xn + BIGDIVREM_EXTRA_WORDS;
- VALUE t = bignew_1(0, tn, 1);
- BDIGIT *tds = BDIGITS(t);
- tn = BIGNUM_LEN(t);
-
- /* t = n/x */
- while (bary_divmod_branch(tds, tn, NULL, 0, nds, len, xds, xn),
- bary_cmp(tds, tn, xds, xn) < 0) {
- int carry;
- BARY_TRUNC(tds, tn);
- /* x = (x+t)/2 */
- carry = bary_add(xds, xn, xds, xn, tds, tn);
- bary_small_rshift(xds, xds, xn, 1, carry);
- tn = BIGNUM_LEN(t);
- }
- rb_big_realloc(t, 0);
- rb_gc_force_recycle(t);
+ size_t tn = xn + BIGDIVREM_EXTRA_WORDS;
+ VALUE t = bignew_1(0, tn, 1);
+ BDIGIT *tds = BDIGITS(t);
+ tn = BIGNUM_LEN(t);
+
+ /* t = n/x */
+ while (bary_divmod_branch(tds, tn, NULL, 0, nds, len, xds, xn),
+ bary_cmp(tds, tn, xds, xn) < 0) {
+ int carry;
+ BARY_TRUNC(tds, tn);
+ /* x = (x+t)/2 */
+ carry = bary_add(xds, xn, xds, xn, tds, tn);
+ bary_small_rshift(xds, xds, xn, 1, carry);
+ tn = BIGNUM_LEN(t);
+ }
}
RBASIC_SET_CLASS_RAW(x, rb_cInteger);
return x;
}
-#ifdef USE_GMP
+#if USE_GMP
static void
bary_powm_gmp(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT *yds, size_t yn, const BDIGIT *mds, size_t mn)
{
- const size_t nails = (sizeof(BDIGIT)-SIZEOF_BDIGIT)*CHAR_BIT;
mpz_t z, x, y, m;
size_t count;
mpz_init(x);
mpz_init(y);
mpz_init(m);
mpz_init(z);
- mpz_import(x, xn, -1, sizeof(BDIGIT), 0, nails, xds);
- mpz_import(y, yn, -1, sizeof(BDIGIT), 0, nails, yds);
- mpz_import(m, mn, -1, sizeof(BDIGIT), 0, nails, mds);
+ bdigits_to_mpz(x, xds, xn);
+ bdigits_to_mpz(y, yds, yn);
+ bdigits_to_mpz(m, mds, mn);
mpz_powm(z, x, y, m);
- mpz_export(zds, &count, -1, sizeof(BDIGIT), 0, nails, z);
+ bdigits_from_mpz(z, zds, &count);
BDIGITS_ZERO(zds+count, zn-count);
mpz_clear(x);
mpz_clear(y);
@@ -6919,7 +6986,7 @@ bary_powm_gmp(BDIGIT *zds, size_t zn, const BDIGIT *xds, size_t xn, const BDIGIT
static VALUE
int_pow_tmp3(VALUE x, VALUE y, VALUE m, int nega_flg)
{
-#ifdef USE_GMP
+#if USE_GMP
VALUE z;
size_t xn, yn, mn, zn;
@@ -7085,6 +7152,7 @@ rb_int_powm(int const argc, VALUE * const argv, VALUE const num)
long const half_val = (long)HALF_LONG_MSB;
long const mm = FIX2LONG(m);
if (!mm) rb_num_zerodiv();
+ if (mm == 1) return INT2FIX(0);
if (mm <= half_val) {
return int_pow_tmp1(rb_int_modulo(a, m), b, mm, nega_flg);
}
@@ -7094,6 +7162,7 @@ rb_int_powm(int const argc, VALUE * const argv, VALUE const num)
}
else {
if (rb_bigzero_p(m)) rb_num_zerodiv();
+ if (bignorm(m) == INT2FIX(1)) return INT2FIX(0);
return int_pow_tmp3(rb_int_modulo(a, m), b, m, nega_flg);
}
}
@@ -7121,16 +7190,9 @@ rb_int_powm(int const argc, VALUE * const argv, VALUE const num)
void
Init_Bignum(void)
{
-#ifndef RUBY_INTEGER_UNIFICATION
- rb_cBignum = rb_cInteger;
-#endif
- /* An obsolete class, use Integer */
- rb_define_const(rb_cObject, "Bignum", rb_cInteger);
- rb_deprecate_constant(rb_cObject, "Bignum");
-
rb_define_method(rb_cInteger, "coerce", rb_int_coerce, 1);
-#ifdef USE_GMP
+#if USE_GMP
/* The version of loaded GMP. */
rb_define_const(rb_cInteger, "GMP_VERSION", rb_sprintf("GMP %s", gmp_version));
#endif
diff --git a/bin/erb b/bin/erb
deleted file mode 100755
index d060caba90..0000000000
--- a/bin/erb
+++ /dev/null
@@ -1,176 +0,0 @@
-#!/usr/bin/env ruby
-# Tiny eRuby --- ERB2
-# Copyright (c) 1999-2000,2002 Masatoshi SEKI
-# You can redistribute it and/or modify it under the same terms as Ruby.
-
-require 'erb'
-
-class ERB
- module Main
- def ARGV.switch
- return nil if self.empty?
- arg = self.shift
- return nil if arg == '--'
- case arg
- when /\A-(.)(.*)/
- if $1 == '-'
- arg, @maybe_arg = arg.split(/=/, 2)
- return arg
- end
- raise 'unknown switch "-"' if $2[0] == ?- and $1 != 'T'
- if $2.size > 0
- self.unshift "-#{$2}"
- @maybe_arg = $2
- else
- @maybe_arg = nil
- end
- "-#{$1}"
- when /\A(\w+)=/
- arg
- else
- self.unshift arg
- nil
- end
- end
-
- def ARGV.req_arg
- (@maybe_arg || self.shift || raise('missing argument')).tap {
- @maybe_arg = nil
- }
- end
-
- def trim_mode_opt(trim_mode, disable_percent)
- return trim_mode if disable_percent
- case trim_mode
- when 0
- return '%'
- when 1
- return '%>'
- when 2
- return '%<>'
- when '-'
- return '%-'
- end
- end
- module_function :trim_mode_opt
-
- def run(factory=ERB)
- trim_mode = 0
- disable_percent = false
- variables = {}
- begin
- while switch = ARGV.switch
- case switch
- when '-x' # ruby source
- output = true
- when '-n' # line number
- number = true
- when '-v' # verbose
- $VERBOSE = true
- when '--version' # version
- STDERR.puts factory.version
- exit
- when '-d', '--debug' # debug
- $DEBUG = true
- when '-r' # require
- require ARGV.req_arg
- when '-S' # security level
- warn 'warning: -S option of erb command is deprecated. Please do not use this.'
- arg = ARGV.req_arg
- raise "invalid safe_level #{arg.dump}" unless arg =~ /\A[0-1]\z/
- safe_level = arg.to_i
- when '-T' # trim mode
- arg = ARGV.req_arg
- if arg == '-'
- trim_mode = arg
- next
- end
- raise "invalid trim mode #{arg.dump}" unless arg =~ /\A[0-2]\z/
- trim_mode = arg.to_i
- when '-E', '--encoding'
- arg = ARGV.req_arg
- set_encoding(*arg.split(/:/, 2))
- when '-U'
- set_encoding(Encoding::UTF_8, Encoding::UTF_8)
- when '-P'
- disable_percent = true
- when '--help'
- raise "print this help"
- when /\A-/
- raise "unknown switch #{switch.dump}"
- else
- var, val = *switch.split('=', 2)
- (variables ||= {})[var] = val
- end
- end
- rescue # usage
- STDERR.puts $!.to_s
- STDERR.puts File.basename($0) +
- " [switches] [var=value...] [inputfile]"
- STDERR.puts <<EOU
- -x print ruby script
- -n print ruby script with line number
- -v enable verbose mode
- -d set $DEBUG to true
- -r library load a library
- -E ex[:in] set default external/internal encodings
- -U set default encoding to UTF-8.
- -T trim_mode specify trim_mode (0..2, -)
- -P ignore lines which start with "%"
- var=value set variable
-EOU
- exit 1
- end
-
- $<.set_encoding(Encoding::UTF_8, nil)
- src = $<.read
- filename = $FILENAME
- exit 2 unless src
- trim = trim_mode_opt(trim_mode, disable_percent)
- if safe_level.nil?
- erb = factory.new(src.untaint, trim_mode: trim)
- else
- # [deprecated] This will be removed at Ruby 2.7.
- erb = factory.new(src.untaint, safe_level, trim_mode: trim)
- end
- erb.filename = filename
- if output
- if number
- erb.src.each_line.with_index do |line, l|
- puts "%3d %s"%[l+1, line]
- end
- else
- puts erb.src
- end
- else
- bind = TOPLEVEL_BINDING.taint
- if variables
- enc = erb.encoding
- for var, val in variables do
- val = val.encode(enc) if val
- bind.local_variable_set(var, val)
- end
- end
- erb.run(bind)
- end
- end
- module_function :run
-
- def set_encoding(extern, intern = nil)
- verbose, $VERBOSE = $VERBOSE, nil
- Encoding.default_external = extern unless extern.nil? || extern == ""
- Encoding.default_internal = intern unless intern.nil? || intern == ""
- [$stdin, $stdout, $stderr].each do |io|
- io.set_encoding(extern, intern)
- end
- ensure
- $VERBOSE = verbose
- end
- module_function :set_encoding
- class << self; private :set_encoding; end
- end
-end
-
-if __FILE__ == $0
- ERB::Main.run
-end
diff --git a/bin/gem b/bin/gem
index a4ec754abb..1c16ea7ddd 100755
--- a/bin/gem
+++ b/bin/gem
@@ -5,21 +5,6 @@
# See LICENSE.txt for permissions.
#++
-require 'rubygems'
-require 'rubygems/gem_runner'
-require 'rubygems/exceptions'
-
-required_version = Gem::Requirement.new ">= 1.8.7"
-
-unless required_version.satisfied_by? Gem.ruby_version then
- abort "Expected Ruby Version #{required_version}, is #{Gem.ruby_version}"
-end
-
-args = ARGV.clone
-
-begin
- Gem::GemRunner.new.run args
-rescue Gem::SystemExitException => e
- exit e.exit_code
-end
+require "rubygems/gem_runner"
+Gem::GemRunner.new.run ARGV.clone
diff --git a/bin/rdoc b/bin/rdoc
deleted file mode 100755
index aaa23292df..0000000000
--- a/bin/rdoc
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env ruby
-#
-# RDoc: Documentation tool for source code
-# (see lib/rdoc/rdoc.rb for more information)
-#
-# Copyright (c) 2003 Dave Thomas
-# Released under the same terms as Ruby
-
-begin
- gem 'rdoc'
-rescue NameError => e # --disable-gems
- raise unless e.name == :gem
-rescue Gem::LoadError
-end
-
-require 'rdoc/rdoc'
-
-begin
- r = RDoc::RDoc.new
- r.document ARGV
-rescue Errno::ENOSPC
- $stderr.puts 'Ran out of space creating documentation'
- $stderr.puts
- $stderr.puts 'Please free up some space and try again'
-rescue SystemExit
- raise
-rescue Exception => e
- if $DEBUG_RDOC then
- $stderr.puts e.message
- $stderr.puts "#{e.backtrace.join "\n\t"}"
- $stderr.puts
- elsif Interrupt === e then
- $stderr.puts
- $stderr.puts 'Interrupted'
- else
- $stderr.puts "uh-oh! RDoc had a problem:"
- $stderr.puts e.message
- $stderr.puts
- $stderr.puts "run with --debug for full backtrace"
- end
-
- exit 1
-end
-
diff --git a/bootstraptest/pending.rb b/bootstraptest/pending.rb
index 744c0adac1..2c4b85a419 100644
--- a/bootstraptest/pending.rb
+++ b/bootstraptest/pending.rb
@@ -1,19 +1,3 @@
-assert_equal 'A', %q{
- class A
- @@a = 'A'
- def a=(x)
- @@a = x
- end
- def a
- @@a
- end
- end
-
- B = A.dup
- B.new.a = 'B'
- A.new.a
-}, '[ruby-core:17019]'
-
assert_equal 'ok', %q{
def m
lambda{
@@ -31,9 +15,7 @@ assert_equal 'ok', %q{
end
}
-assert_normal_exit %q{
- r = Range.allocate
- def r.<=>(o) true end
- r.instance_eval { initialize r, r }
- r.inspect
+# This randomly fails on mswin.
+assert_equal %q{[]}, %q{
+ Thread.new{sleep}.backtrace
}
diff --git a/bootstraptest/runner.rb b/bootstraptest/runner.rb
index a5d5d219cf..f9b3e919b8 100755
--- a/bootstraptest/runner.rb
+++ b/bootstraptest/runner.rb
@@ -1,4 +1,4 @@
-"exec" "${RUBY-ruby}" "-x" "$0" "$@" || true # -*- mode: ruby; coding: utf-8 -*-
+"exec" "${RUBY-ruby}" "-x" "$0" "$@" || true # -*- Ruby -*-
#!./ruby
# $Id$
@@ -8,6 +8,8 @@
# Never use Ruby extensions in this file.
# Maintain Ruby 1.8 compatibility for now
+$start_time = Time.now
+
begin
require 'fileutils'
require 'tmpdir'
@@ -58,24 +60,114 @@ if !Dir.respond_to?(:mktmpdir)
end
end
+# Configuration
+bt = Struct.new(:ruby,
+ :verbose,
+ :color,
+ :tty,
+ :quiet,
+ :wn,
+ :progress,
+ :progress_bs,
+ :passed,
+ :failed,
+ :reset,
+ :columns,
+ :window_width,
+ :width,
+ :indent,
+ :platform,
+ )
+BT = Class.new(bt) do
+ def indent=(n)
+ super
+ if (self.columns ||= 0) < n
+ $stderr.print(' ' * (n - self.columns))
+ end
+ self.columns = indent
+ end
+
+ def putc(c)
+ unless self.quiet
+ if self.window_width == nil
+ unless w = ENV["COLUMNS"] and (w = w.to_i) > 0
+ w = 80
+ end
+ w -= 1
+ self.window_width = w
+ end
+ if self.window_width and self.columns >= self.window_width
+ $stderr.print "\n", " " * (self.indent ||= 0)
+ self.columns = indent
+ end
+ $stderr.print c
+ $stderr.flush
+ self.columns += 1
+ end
+ end
+
+ def wn=(wn)
+ unless wn == 1
+ if /(?:\A|\s)--jobserver-(?:auth|fds)=(?:(\d+),(\d+)|fifo:((?:\\.|\S)+))/ =~ ENV.delete("MAKEFLAGS")
+ begin
+ if fifo = $3
+ fifo.gsub!(/\\(?=.)/, '')
+ r = File.open(fifo, IO::RDONLY|IO::NONBLOCK|IO::BINARY)
+ w = File.open(fifo, IO::WRONLY|IO::NONBLOCK|IO::BINARY)
+ else
+ r = IO.for_fd($1.to_i(10), "rb", autoclose: false)
+ w = IO.for_fd($2.to_i(10), "wb", autoclose: false)
+ end
+ rescue => e
+ r.close if r
+ else
+ r.close_on_exec = true
+ w.close_on_exec = true
+ tokens = r.read_nonblock(wn > 0 ? wn : 1024, exception: false)
+ r.close
+ if String === tokens
+ tokens.freeze
+ auth = w
+ w = nil
+ at_exit {auth << tokens; auth.close}
+ wn = tokens.size + 1
+ else
+ w.close
+ wn = 1
+ end
+ end
+ end
+ if wn <= 0
+ require 'etc'
+ wn = [Etc.nprocessors / 2, 1].max
+ end
+ end
+ super wn
+ end
+end.new
+
+BT_STATE = Struct.new(:count, :error).new
+
def main
- @ruby = File.expand_path('miniruby')
- @verbose = false
+ BT.ruby = File.expand_path('miniruby')
+ BT.verbose = false
$VERBOSE = false
$stress = false
- @color = nil
- @tty = nil
- @quiet = false
+ BT.color = nil
+ BT.tty = nil
+ BT.quiet = false
+ # BT.wn = 1
dir = nil
quiet = false
tests = nil
ARGV.delete_if {|arg|
case arg
when /\A--ruby=(.*)/
- @ruby = $1
- @ruby.gsub!(/^([^ ]*)/){File.expand_path($1)}
- @ruby.gsub!(/(\s+-I\s*)((?!(?:\.\/)*-(?:\s|\z))\S+)/){$1+File.expand_path($2)}
- @ruby.gsub!(/(\s+-r\s*)(\.\.?\/\S+)/){$1+File.expand_path($2)}
+ ruby = $1
+ ruby.gsub!(/^([^ ]*)/){File.expand_path($1)}
+ ruby.gsub!(/(\s+-I\s*)((?!(?:\.\/)*-(?:\s|\z))\S+)/){$1+File.expand_path($2)}
+ ruby.gsub!(/(\s+-r\s*)(\.\.?\/\S+)/){$1+File.expand_path($2)}
+ BT.ruby = ruby
true
when /\A--sets=(.*)/
tests = Dir.glob("#{File.dirname($0)}/test_{#{$1}}*.rb").sort
@@ -88,18 +180,23 @@ def main
$stress = true
when /\A--color(?:=(?:always|(auto)|(never)|(.*)))?\z/
warn "unknown --color argument: #$3" if $3
- @color = $1 ? nil : !$2
+ BT.color = color = $1 ? nil : !$2
true
when /\A--tty(=(?:yes|(no)|(.*)))?\z/
warn "unknown --tty argument: #$3" if $3
- @tty = !$1 || !$2
+ BT.tty = !$1 || !$2
true
when /\A(-q|--q(uiet))\z/
quiet = true
- @quiet = true
+ BT.quiet = true
+ true
+ when /\A-j(\d+)?/
+ BT.wn = $1.to_i
true
when /\A(-v|--v(erbose))\z/
- @verbose = true
+ BT.verbose = true
+ BT.quiet = false
+ true
when /\A(-h|--h(elp)?)\z/
puts(<<-End)
Usage: #{File.basename($0, '.*')} --ruby=PATH [--sets=NAME,NAME,...]
@@ -121,38 +218,41 @@ End
end
}
if tests and not ARGV.empty?
- $stderr.puts "--tests and arguments are exclusive"
- exit false
+ abort "--sets and arguments are exclusive"
end
tests ||= ARGV
tests = Dir.glob("#{File.dirname($0)}/test_*.rb").sort if tests.empty?
pathes = tests.map {|path| File.expand_path(path) }
- @progress = %w[- \\ | /]
- @progress_bs = "\b" * @progress[0].size
- @tty = $stderr.tty? if @tty.nil?
- case @color
+ BT.progress = %w[- \\ | /]
+ BT.progress_bs = "\b" * BT.progress[0].size
+ BT.tty = $stderr.tty? if BT.tty.nil?
+ BT.wn ||= /-j(\d+)?/ =~ (ENV["MAKEFLAGS"] || ENV["MFLAGS"]) ? $1.to_i : 1
+
+ case BT.color
when nil
- @color = @tty && /dumb/ !~ ENV["TERM"]
+ BT.color = BT.tty && /dumb/ !~ ENV["TERM"]
end
- @tty &&= !@verbose
- if @color
+ BT.tty &&= !BT.verbose
+ if BT.color
# dircolors-like style
colors = (colors = ENV['TEST_COLORS']) ? Hash[colors.scan(/(\w+)=([^:\n]*)/)] : {}
begin
- File.read(File.join(__dir__, "../test/colors")).scan(/(\w+)=([^:\n]*)/) do |n, c|
+ File.read(File.join(__dir__, "../tool/colors")).scan(/(\w+)=([^:\n]*)/) do |n, c|
colors[n] ||= c
end
rescue
end
- @passed = "\e[;#{colors["pass"] || "32"}m"
- @failed = "\e[;#{colors["fail"] || "31"}m"
- @reset = "\e[m"
+ BT.passed = "\e[;#{colors["pass"] || "32"}m"
+ BT.failed = "\e[;#{colors["fail"] || "31"}m"
+ BT.reset = "\e[m"
else
- @passed = @failed = @reset = ""
+ BT.passed = BT.failed = BT.reset = ""
end
+ target_version = `#{BT.ruby} -v`.chomp
+ BT.platform = target_version[/\[(.*)\]\z/, 1]
unless quiet
- puts Time.now
+ puts $start_time
if defined?(RUBY_DESCRIPTION)
puts "Driver is #{RUBY_DESCRIPTION}"
elsif defined?(RUBY_PATCHLEVEL)
@@ -160,253 +260,469 @@ End
else
puts "Driver is ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
end
- puts "Target is #{`#{@ruby} -v`.chomp}"
+ puts "Target is #{target_version}"
puts
$stdout.flush
end
- in_temporary_working_directory(dir) {
+ in_temporary_working_directory(dir) do
exec_test pathes
- }
+ end
end
def erase(e = true)
- if e and @columns > 0 and !@verbose
- "\r#{" "*@columns}\r"
+ if e and BT.columns > 0 and BT.tty and !BT.verbose
+ "\e[1K\r"
else
""
end
end
-def exec_test(pathes)
- @count = 0
- @error = 0
- @errbuf = []
- @location = nil
- @columns = 0
- @width = pathes.map {|path| File.basename(path).size}.max + 2
+def load_test pathes
pathes.each do |path|
- @basename = File.basename(path)
- $stderr.printf("%s%-*s ", erase(@quiet), @width, @basename)
- $stderr.flush
- @columns = @width + 1
- $stderr.puts if @verbose
- count = @count
- error = @error
load File.expand_path(path)
- if @tty
- if @error == error
- msg = "PASS #{@count-count}"
- @columns += msg.size - 1
- $stderr.print "#{@progress_bs}#{@passed}#{msg}#{@reset}"
+ end
+end
+
+def concurrent_exec_test
+ aq = Queue.new
+ rq = Queue.new
+
+ ts = BT.wn.times.map do
+ Thread.new do
+ while as = aq.pop
+ as.call
+ rq << as
+ end
+ ensure
+ rq << nil
+ end
+ end
+
+ Assertion.all.to_a.shuffle.each do |path, assertions|
+ assertions.each do |as|
+ aq << as
+ end
+ end
+
+ BT.indent = 1
+ aq.close
+ i = 1
+ term_wn = 0
+ begin
+ while BT.wn != term_wn
+ if r = rq.pop
+ case
+ when BT.quiet
+ when BT.tty
+ $stderr.print "#{BT.progress_bs}#{BT.progress[(i+=1) % BT.progress.size]}"
+ else
+ BT.putc '.'
+ end
else
- msg = "FAIL #{@error-error}/#{@count-count}"
- $stderr.print "#{@progress_bs}#{@failed}#{msg}#{@reset}"
- @columns = 0
+ term_wn += 1
+ end
+ end
+ ensure
+ ts.each(&:kill)
+ ts.each(&:join)
+ end
+end
+
+def exec_test(pathes)
+ # setup
+ load_test pathes
+ BT_STATE.count = 0
+ BT_STATE.error = 0
+ BT.columns = 0
+ BT.width = pathes.map {|path| File.basename(path).size}.max + 2
+
+ # execute tests
+ if BT.wn > 1
+ concurrent_exec_test
+ else
+ prev_basename = nil
+ Assertion.all.each do |basename, assertions|
+ if !BT.quiet && basename != prev_basename
+ prev_basename = basename
+ $stderr.printf("%s%-*s ", erase(BT.quiet), BT.width, basename)
+ $stderr.flush
+ end
+ BT.columns = BT.width + 1
+ $stderr.puts if BT.verbose
+ count = BT_STATE.count
+ error = BT_STATE.error
+
+ assertions.each do |assertion|
+ BT_STATE.count += 1
+ assertion.call
+ end
+
+ if BT.tty
+ if BT_STATE.error == error
+ msg = "PASS #{BT_STATE.count-count}"
+ BT.columns += msg.size - 1
+ $stderr.print "#{BT.progress_bs}#{BT.passed}#{msg}#{BT.reset}" unless BT.quiet
+ else
+ msg = "FAIL #{BT_STATE.error-error}/#{BT_STATE.count-count}"
+ $stderr.print "#{BT.progress_bs}#{BT.failed}#{msg}#{BT.reset}"
+ BT.columns = 0
+ end
end
+ $stderr.puts if !BT.quiet and (BT.tty or BT_STATE.error == error)
end
- $stderr.puts unless @quiet and @tty and @error == error
end
- $stderr.print(erase) if @quiet
- if @error == 0
- if @count == 0
- $stderr.puts "No tests, no problem"
+
+ # show results
+ unless BT.quiet
+ $stderr.puts(erase)
+
+ sec = Time.now - $start_time
+ $stderr.puts "Finished in #{'%.2f' % sec} sec\n\n" if Assertion.count > 0
+ end
+
+ Assertion.errbuf.each do |msg|
+ $stderr.puts msg
+ end
+
+ out = BT.quiet ? $stdout : $stderr
+
+ if BT_STATE.error == 0
+ if Assertion.count == 0
+ out.puts "No tests, no problem" unless BT.quiet
else
- $stderr.puts "#{@passed}PASS#{@reset} all #{@count} tests"
+ out.puts "#{BT.passed}PASS#{BT.reset} all #{Assertion.count} tests"
end
- exit true
+ true
else
- @errbuf.each do |msg|
- $stderr.puts msg
- end
- $stderr.puts "#{@failed}FAIL#{@reset} #{@error}/#{@count} tests failed"
- exit false
+ $stderr.puts "#{BT.failed}FAIL#{BT.reset} #{BT_STATE.error}/#{BT_STATE.count} tests failed"
+ false
end
end
-def show_progress(message = '')
- if @verbose
- $stderr.print "\##{@count} #{@location} "
- elsif @tty
- $stderr.print "#{@progress_bs}#{@progress[@count % @progress.size]}"
- end
- t = Time.now if @verbose
- faildesc, errout = with_stderr {yield}
- t = Time.now - t if @verbose
- if !faildesc
- if @tty
- $stderr.print "#{@progress_bs}#{@progress[@count % @progress.size]}"
- elsif @verbose
- $stderr.printf(". %.3f\n", t)
+def target_platform
+ BT.platform or RUBY_PLATFORM
+end
+
+class Assertion < Struct.new(:src, :path, :lineno, :proc)
+ @count = 0
+ @all = Hash.new{|h, k| h[k] = []}
+ @errbuf = []
+
+ class << self
+ attr_reader :count, :errbuf
+
+ def all
+ @all
+ end
+
+ def add as
+ @all[as.path] << as
+ as.id = (@count += 1)
+ end
+ end
+
+ attr_accessor :id
+ attr_reader :err, :category
+
+ def initialize(*args)
+ super
+ self.class.add self
+ @category = self.path.match(/test_(.+)\.rb/)[1]
+ end
+
+ def call
+ self.proc.call self
+ end
+
+ def assert_check(message = '', opt = '', **argh)
+ show_progress(message) {
+ result = get_result_string(opt, **argh)
+ yield(result)
+ }
+ end
+
+ def with_stderr
+ out = err = nil
+ r, w = IO.pipe
+ @err = w
+ err_reader = Thread.new{ r.read }
+
+ begin
+ out = yield
+ ensure
+ w.close
+ err = err_reader.value
+ r.close rescue nil
+ end
+
+ return out, err
+ end
+
+ def show_error(msg, additional_message)
+ msg = "#{BT.failed}\##{self.id} #{self.path}:#{self.lineno}#{BT.reset}: #{msg} #{additional_message}"
+ if BT.tty
+ $stderr.puts "#{erase}#{msg}"
else
- $stderr.print '.'
+ Assertion.errbuf << msg
end
- else
- $stderr.print "#{@failed}F"
- $stderr.printf(" %.3f", t) if @verbose
- $stderr.print "#{@reset}"
- $stderr.puts if @verbose
- error faildesc, message
- unless errout.empty?
- $stderr.print "#{@failed}stderr output is not empty#{@reset}\n", adjust_indent(errout)
+ BT_STATE.error += 1
+ end
+
+
+ def show_progress(message = '')
+ if BT.quiet || BT.wn > 1
+ # do nothing
+ elsif BT.verbose
+ $stderr.print "\##{@id} #{self.path}:#{self.lineno} "
+ elsif BT.tty
+ $stderr.print "#{BT.progress_bs}#{BT.progress[BT_STATE.count % BT.progress.size]}"
+ end
+
+ t = Time.now if BT.verbose
+ faildesc, errout = with_stderr {yield}
+ t = Time.now - t if BT.verbose
+
+ if !faildesc
+ # success
+ if BT.quiet || BT.wn > 1
+ # do nothing
+ elsif BT.tty
+ $stderr.print "#{BT.progress_bs}#{BT.progress[BT_STATE.count % BT.progress.size]}"
+ elsif BT.verbose
+ $stderr.printf(". %.3f\n", t)
+ else
+ BT.putc '.'
+ end
+ else
+ $stderr.print "#{BT.failed}F"
+ $stderr.printf(" %.3f", t) if BT.verbose
+ $stderr.print BT.reset
+ $stderr.puts if BT.verbose
+ show_error faildesc, message
+ unless errout.empty?
+ $stderr.print "#{BT.failed}stderr output is not empty#{BT.reset}\n", adjust_indent(errout)
+ end
+
+ if BT.tty and !BT.verbose and BT.wn == 1
+ $stderr.printf("%-*s%s", BT.width, path, BT.progress[BT_STATE.count % BT.progress.size])
+ end
+ end
+ rescue Interrupt
+ $stderr.puts "\##{@id} #{path}:#{lineno}"
+ raise
+ rescue Exception => err
+ $stderr.print 'E'
+ $stderr.puts if BT.verbose
+ show_error err.message, message
+ ensure
+ begin
+ check_coredump
+ rescue CoreDumpError => err
+ $stderr.print 'E'
+ $stderr.puts if BT.verbose
+ show_error err.message, message
+ cleanup_coredump
end
- if @tty and !@verbose
- $stderr.printf("%-*s%s", @width, @basename, @progress[@count % @progress.size])
+ end
+
+ def get_result_string(opt = '', **argh)
+ if BT.ruby
+ filename = make_srcfile(**argh)
+ begin
+ kw = self.err ? {err: self.err} : {}
+ out = IO.popen("#{BT.ruby} -W0 #{opt} #{filename}", **kw)
+ pid = out.pid
+ out.read.tap{ Process.waitpid(pid); out.close }
+ ensure
+ raise Interrupt if $? and $?.signaled? && $?.termsig == Signal.list["INT"]
+
+ begin
+ Process.kill :KILL, pid
+ rescue Errno::ESRCH
+ # OK
+ end
+ end
+ else
+ eval(src).to_s
end
end
-rescue Interrupt
- $stderr.puts "\##{@count} #{@location}"
- raise
-rescue Exception => err
- $stderr.print 'E'
- $stderr.puts if @verbose
- error err.message, message
+
+ def make_srcfile(frozen_string_literal: nil)
+ filename = "bootstraptest.#{self.path}_#{self.lineno}_#{self.id}.rb"
+ File.open(filename, 'w') {|f|
+ f.puts "#frozen_string_literal:true" if frozen_string_literal
+ f.puts "GC.stress = true" if $stress
+ f.puts "print(begin; #{self.src}; end)"
+ }
+ filename
+ end
end
-def assert_check(testsrc, message = '', opt = '', **argh)
- show_progress(message) {
- result = get_result_string(testsrc, opt, **argh)
- check_coredump
- yield(result)
- }
+def add_assertion src, pr
+ loc = caller_locations(2, 1).first
+ lineno = loc.lineno
+ path = File.basename(loc.path)
+
+ Assertion.new(src, path, lineno, pr)
end
def assert_equal(expected, testsrc, message = '', opt = '', **argh)
- newtest
- assert_check(testsrc, message, opt, **argh) {|result|
- if expected == result
- nil
- else
- desc = "#{result.inspect} (expected #{expected.inspect})"
- pretty(testsrc, desc, result)
- end
- }
+ add_assertion testsrc, -> as do
+ as.assert_check(message, opt, **argh) {|result|
+ if expected == result
+ nil
+ else
+ desc = "#{result.inspect} (expected #{expected.inspect})"
+ pretty(testsrc, desc, result)
+ end
+ }
+ end
end
def assert_match(expected_pattern, testsrc, message = '')
- newtest
- assert_check(testsrc, message) {|result|
- if expected_pattern =~ result
- nil
- else
- desc = "#{expected_pattern.inspect} expected to be =~\n#{result.inspect}"
- pretty(testsrc, desc, result)
- end
- }
+ add_assertion testsrc, -> as do
+ as.assert_check(message) {|result|
+ if expected_pattern =~ result
+ nil
+ else
+ desc = "#{expected_pattern.inspect} expected to be =~\n#{result.inspect}"
+ pretty(testsrc, desc, result)
+ end
+ }
+ end
end
def assert_not_match(unexpected_pattern, testsrc, message = '')
- newtest
- assert_check(testsrc, message) {|result|
- if unexpected_pattern !~ result
- nil
- else
- desc = "#{unexpected_pattern.inspect} expected to be !~\n#{result.inspect}"
- pretty(testsrc, desc, result)
- end
- }
+ add_assertion testsrc, -> as do
+ as.assert_check(message) {|result|
+ if unexpected_pattern !~ result
+ nil
+ else
+ desc = "#{unexpected_pattern.inspect} expected to be !~\n#{result.inspect}"
+ pretty(testsrc, desc, result)
+ end
+ }
+ end
end
def assert_valid_syntax(testsrc, message = '')
- newtest
- assert_check(testsrc, message, '-c') {|result|
- result if /Syntax OK/ !~ result
- }
+ add_assertion testsrc, -> as do
+ as.assert_check(message, '-c') {|result|
+ result if /Syntax OK/ !~ result
+ }
+ end
end
def assert_normal_exit(testsrc, *rest, timeout: nil, **opt)
- newtest
- message, ignore_signals = rest
- message ||= ''
- show_progress(message) {
- faildesc = nil
- filename = make_srcfile(testsrc)
- old_stderr = $stderr.dup
- timeout_signaled = false
- begin
- $stderr.reopen("assert_normal_exit.log", "w")
- io = IO.popen("#{@ruby} -W0 #{filename}")
- pid = io.pid
- th = Thread.new {
- io.read
- io.close
- $?
- }
- if !th.join(timeout)
- Process.kill :KILL, pid
- timeout_signaled = true
- end
- status = th.value
- ensure
- $stderr.reopen(old_stderr)
- old_stderr.close
- end
- if status && status.signaled?
- signo = status.termsig
- signame = Signal.list.invert[signo]
- unless ignore_signals and ignore_signals.include?(signame)
- sigdesc = "signal #{signo}"
- if signame
- sigdesc = "SIG#{signame} (#{sigdesc})"
- end
- if timeout_signaled
- sigdesc << " (timeout)"
+ add_assertion testsrc, -> as do
+ message, ignore_signals = rest
+ message ||= ''
+ as.show_progress(message) {
+ faildesc = nil
+ filename = as.make_srcfile
+ timeout_signaled = false
+ logfile = "assert_normal_exit.#{as.path}.#{as.lineno}.log"
+
+ begin
+ err = open(logfile, "w")
+ io = IO.popen("#{BT.ruby} -W0 #{filename}", err: err)
+ pid = io.pid
+ th = Thread.new {
+ io.read
+ io.close
+ $?
+ }
+ if !th.join(timeout)
+ Process.kill :KILL, pid
+ timeout_signaled = true
end
- faildesc = pretty(testsrc, "killed by #{sigdesc}", nil)
- stderr_log = File.read("assert_normal_exit.log")
- if !stderr_log.empty?
- faildesc << "\n" if /\n\z/ !~ faildesc
- stderr_log << "\n" if /\n\z/ !~ stderr_log
- stderr_log.gsub!(/^.*\n/) { '| ' + $& }
- faildesc << stderr_log
+ status = th.value
+ ensure
+ err.close
+ end
+ if status && status.signaled?
+ signo = status.termsig
+ signame = Signal.list.invert[signo]
+ unless ignore_signals and ignore_signals.include?(signame)
+ sigdesc = "signal #{signo}"
+ if signame
+ sigdesc = "SIG#{signame} (#{sigdesc})"
+ end
+ if timeout_signaled
+ sigdesc << " (timeout)"
+ end
+ faildesc = pretty(testsrc, "killed by #{sigdesc}", nil)
+ stderr_log = File.read(logfile)
+ if !stderr_log.empty?
+ faildesc << "\n" if /\n\z/ !~ faildesc
+ stderr_log << "\n" if /\n\z/ !~ stderr_log
+ stderr_log.gsub!(/^.*\n/) { '| ' + $& }
+ faildesc << stderr_log
+ end
end
end
- end
- faildesc
- }
+ faildesc
+ }
+ end
end
def assert_finish(timeout_seconds, testsrc, message = '')
- newtest
- show_progress(message) {
- faildesc = nil
- filename = make_srcfile(testsrc)
- io = IO.popen("#{@ruby} -W0 #{filename}")
- pid = io.pid
- waited = false
- tlimit = Time.now + timeout_seconds
- diff = timeout_seconds
- while diff > 0
- if Process.waitpid pid, Process::WNOHANG
- waited = true
- break
- end
- if io.respond_to?(:read_nonblock)
- if IO.select([io], nil, nil, diff)
- begin
- io.read_nonblock(1024)
- rescue Errno::EAGAIN, EOFError
- break
- end while true
+ add_assertion testsrc, -> as do
+ if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ timeout_seconds *= 3
+ end
+
+ as.show_progress(message) {
+ faildesc = nil
+ filename = as.make_srcfile
+ io = IO.popen("#{BT.ruby} -W0 #{filename}", err: as.err)
+ pid = io.pid
+ waited = false
+ tlimit = Time.now + timeout_seconds
+ diff = timeout_seconds
+ while diff > 0
+ if Process.waitpid pid, Process::WNOHANG
+ waited = true
+ break
end
- else
- sleep 0.1
+ if io.respond_to?(:read_nonblock)
+ if IO.select([io], nil, nil, diff)
+ begin
+ io.read_nonblock(1024)
+ rescue Errno::EAGAIN, IO::WaitReadable, EOFError
+ break
+ end while true
+ end
+ else
+ sleep 0.1
+ end
+ diff = tlimit - Time.now
end
- diff = tlimit - Time.now
- end
- if !waited
- Process.kill(:KILL, pid)
- Process.waitpid pid
- faildesc = pretty(testsrc, "not finished in #{timeout_seconds} seconds", nil)
- end
- io.close
- faildesc
- }
+ if !waited
+ Process.kill(:KILL, pid)
+ Process.waitpid pid
+ faildesc = pretty(testsrc, "not finished in #{timeout_seconds} seconds", nil)
+ end
+ io.close
+ faildesc
+ }
+ end
end
def flunk(message = '')
- newtest
- show_progress('') { message }
+ add_assertion '', -> as do
+ as.show_progress('') { message }
+ end
+end
+
+def show_limit(testsrc, opt = '', **argh)
+ return if BT.quiet
+
+ add_assertion testsrc, -> as do
+ result = as.get_result_string(opt, **argh)
+ Assertion.errbuf << result
+ end
end
def pretty(src, desc, result)
@@ -424,67 +740,6 @@ def untabify(str)
str.gsub(/^\t+/) {' ' * (8 * $&.size) }
end
-def make_srcfile(src, frozen_string_literal: nil)
- filename = 'bootstraptest.tmp.rb'
- File.open(filename, 'w') {|f|
- f.puts "#frozen_string_literal:true" if frozen_string_literal
- f.puts "GC.stress = true" if $stress
- f.puts "print(begin; #{src}; end)"
- }
- filename
-end
-
-def get_result_string(src, opt = '', **argh)
- if @ruby
- filename = make_srcfile(src, **argh)
- begin
- `#{@ruby} -W0 #{opt} #{filename}`
- ensure
- raise Interrupt if $? and $?.signaled? && $?.termsig == Signal.list["INT"]
- raise CoreDumpError, "core dumped" if $? and $?.coredump?
- end
- else
- eval(src).to_s
- end
-end
-
-def with_stderr
- out = err = nil
- begin
- r, w = IO.pipe
- stderr = $stderr.dup
- $stderr.reopen(w)
- w.close
- reader = Thread.start {r.read}
- begin
- out = yield
- ensure
- $stderr.reopen(stderr)
- err = reader.value
- end
- ensure
- w.close rescue nil
- r.close rescue nil
- end
- return out, err
-end
-
-def newtest
- @location = File.basename(caller(2).first)
- @count += 1
- cleanup_coredump
-end
-
-def error(msg, additional_message)
- msg = "#{@failed}\##{@count} #{@location}#{@reset}: #{msg} #{additional_message}"
- if @tty
- $stderr.puts "#{erase}#{msg}"
- else
- @errbuf.push msg
- end
- @error += 1
-end
-
def in_temporary_working_directory(dir)
if dir
Dir.mkdir dir
@@ -501,18 +756,32 @@ def in_temporary_working_directory(dir)
end
def cleanup_coredump
- FileUtils.rm_f 'core'
+ if File.file?('core')
+ require 'time'
+ Dir.glob('/tmp/bootstraptest-core.*').each do |f|
+ if Time.now - File.mtime(f) > 7 * 24 * 60 * 60 # 7 days
+ warn "Deleting an old core file: #{f}"
+ FileUtils.rm(f)
+ end
+ end
+ core_path = "/tmp/bootstraptest-core.#{Time.now.utc.iso8601}"
+ warn "A core file is found. Saving it at: #{core_path.dump}"
+ FileUtils.mv('core', core_path)
+ cmd = ['gdb', BT.ruby, '-c', core_path, '-ex', 'bt', '-batch']
+ p cmd # debugging why it's not working
+ system(*cmd)
+ end
FileUtils.rm_f Dir.glob('core.*')
- FileUtils.rm_f @ruby+'.stackdump' if @ruby
+ FileUtils.rm_f BT.ruby+'.stackdump' if BT.ruby
end
class CoreDumpError < StandardError; end
def check_coredump
if File.file?('core') or not Dir.glob('core.*').empty? or
- (@ruby and File.exist?(@ruby+'.stackdump'))
+ (BT.ruby and File.exist?(BT.ruby+'.stackdump'))
raise CoreDumpError, "core dumped"
end
end
-main
+exit main
diff --git a/bootstraptest/test_attr.rb b/bootstraptest/test_attr.rb
index 721a847145..3cb9d3eb39 100644
--- a/bootstraptest/test_attr.rb
+++ b/bootstraptest/test_attr.rb
@@ -34,3 +34,19 @@ assert_equal %{ok}, %{
print "ok"
end
}, '[ruby-core:15120]'
+
+assert_equal %{ok}, %{
+ class Big
+ attr_reader :foo
+ def initialize
+ @foo = "ok"
+ end
+ end
+
+ obj = Big.new
+ 100.times do |i|
+ obj.instance_variable_set(:"@ivar_\#{i}", i)
+ end
+
+ Big.new.foo
+}
diff --git a/bootstraptest/test_autoload.rb b/bootstraptest/test_autoload.rb
index a9f8e6dacd..9e0850bc52 100644
--- a/bootstraptest/test_autoload.rb
+++ b/bootstraptest/test_autoload.rb
@@ -1,7 +1,7 @@
assert_equal 'ok', %q{
- File.unlink('zzz.rb') if File.file?('zzz.rb')
+ File.unlink('zzz1.rb') if File.file?('zzz1.rb')
instance_eval do
- autoload :ZZZ, './zzz.rb'
+ autoload :ZZZ, './zzz1.rb'
begin
ZZZ
rescue LoadError
@@ -11,9 +11,9 @@ assert_equal 'ok', %q{
}, '[ruby-dev:43816]'
assert_equal 'ok', %q{
- open('zzz.rb', 'w') {|f| f.puts '' }
+ open('zzz2.rb', 'w') {|f| f.puts '' }
instance_eval do
- autoload :ZZZ, './zzz.rb'
+ autoload :ZZZ, './zzz2.rb'
begin
ZZZ
rescue NameError
@@ -23,29 +23,29 @@ assert_equal 'ok', %q{
}, '[ruby-dev:43816]'
assert_equal 'ok', %q{
- open('zzz.rb', 'w') {|f| f.puts 'class ZZZ; def self.ok;:ok;end;end'}
+ open('zzz3.rb', 'w') {|f| f.puts 'class ZZZ; def self.ok;:ok;end;end'}
instance_eval do
- autoload :ZZZ, './zzz.rb'
+ autoload :ZZZ, './zzz3.rb'
ZZZ.ok
end
}, '[ruby-dev:43816]'
assert_equal 'ok', %q{
- open("zzz.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
- autoload :ZZZ, "./zzz.rb"
+ open("zzz4.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
+ autoload :ZZZ, "./zzz4.rb"
ZZZ.ok
}
assert_equal 'ok', %q{
- open("zzz.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
- autoload :ZZZ, "./zzz.rb"
- require "./zzz.rb"
+ open("zzz5.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
+ autoload :ZZZ, "./zzz5.rb"
+ require "./zzz5.rb"
ZZZ.ok
}
assert_equal 'okok', %q{
- open("zzz.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
- autoload :ZZZ, "./zzz.rb"
+ open("zzz6.rb", "w") {|f| f.puts "class ZZZ; def self.ok;:ok;end;end"}
+ autoload :ZZZ, "./zzz6.rb"
t1 = Thread.new {ZZZ.ok}
t2 = Thread.new {ZZZ.ok}
[t1.value, t2.value].join
@@ -60,9 +60,9 @@ assert_finish 5, %q{
}, '[ruby-core:21696]'
assert_equal 'A::C', %q{
- open("zzz.rb", "w") {}
+ open("zzz7.rb", "w") {}
class A
- autoload :C, "./zzz"
+ autoload :C, "./zzz7"
class C
end
C
diff --git a/bootstraptest/test_constant_cache.rb b/bootstraptest/test_constant_cache.rb
new file mode 100644
index 0000000000..1fa83256ed
--- /dev/null
+++ b/bootstraptest/test_constant_cache.rb
@@ -0,0 +1,187 @@
+# Constant lookup is cached.
+assert_equal '1', %q{
+ CONST = 1
+
+ def const
+ CONST
+ end
+
+ const
+ const
+}
+
+# Invalidate when a constant is set.
+assert_equal '2', %q{
+ CONST = 1
+
+ def const
+ CONST
+ end
+
+ const
+
+ CONST = 2
+
+ const
+}
+
+# Invalidate when a constant of the same name is set.
+assert_equal '1', %q{
+ CONST = 1
+
+ def const
+ CONST
+ end
+
+ const
+
+ class Container
+ CONST = 2
+ end
+
+ const
+}
+
+# Invalidate when a constant is removed.
+assert_equal 'missing', %q{
+ class Container
+ CONST = 1
+
+ def const
+ CONST
+ end
+
+ def self.const_missing(name)
+ 'missing'
+ end
+
+ new.const
+ remove_const :CONST
+ end
+
+ Container.new.const
+}
+
+# Invalidate when a constant's visibility changes.
+assert_equal 'missing', %q{
+ class Container
+ CONST = 1
+
+ def self.const_missing(name)
+ 'missing'
+ end
+ end
+
+ def const
+ Container::CONST
+ end
+
+ const
+
+ Container.private_constant :CONST
+
+ const
+}
+
+# Invalidate when a constant's visibility changes even if the call to the
+# visibility change method fails.
+assert_equal 'missing', %q{
+ class Container
+ CONST1 = 1
+
+ def self.const_missing(name)
+ 'missing'
+ end
+ end
+
+ def const1
+ Container::CONST1
+ end
+
+ const1
+
+ begin
+ Container.private_constant :CONST1, :CONST2
+ rescue NameError
+ end
+
+ const1
+}
+
+# Invalidate when a module is included.
+assert_equal 'INCLUDE', %q{
+ module Include
+ CONST = :INCLUDE
+ end
+
+ class Parent
+ CONST = :PARENT
+ end
+
+ class Child < Parent
+ def const
+ CONST
+ end
+
+ new.const
+
+ include Include
+ end
+
+ Child.new.const
+}
+
+# Invalidate when const_missing is hit.
+assert_equal '2', %q{
+ module Container
+ Foo = 1
+ Bar = 2
+
+ class << self
+ attr_accessor :count
+
+ def const_missing(name)
+ @count += 1
+ @count == 1 ? Foo : Bar
+ end
+ end
+
+ @count = 0
+ end
+
+ def const
+ Container::Baz
+ end
+
+ const
+ const
+}
+
+# Invalidate when the iseq gets cleaned up.
+assert_equal '2', %q{
+ CONSTANT = 1
+
+ iseq = RubyVM::InstructionSequence.compile(<<~RUBY)
+ CONSTANT
+ RUBY
+
+ iseq.eval
+ iseq = nil
+
+ GC.start
+ CONSTANT = 2
+}
+
+# Invalidate when the iseq gets cleaned up even if it was never in the cache.
+assert_equal '2', %q{
+ CONSTANT = 1
+
+ iseq = RubyVM::InstructionSequence.compile(<<~RUBY)
+ CONSTANT
+ RUBY
+
+ iseq = nil
+
+ GC.start
+ CONSTANT = 2
+}
diff --git a/bootstraptest/test_env.rb b/bootstraptest/test_env.rb
new file mode 100644
index 0000000000..7d1b45b75e
--- /dev/null
+++ b/bootstraptest/test_env.rb
@@ -0,0 +1,12 @@
+assert_equal "true", %q{
+ ENV["ENVTEST"] = "\u{e9 3042 d76c}"
+ env = ENV["ENVTEST"]
+ env.valid_encoding?
+}
+
+# different encoding is used for PATH
+assert_equal "true", %q{
+ ENV["PATH"] = "\u{e9 3042 d76c}"
+ env = ENV["PATH"]
+ env.valid_encoding?
+}
diff --git a/bootstraptest/test_eval.rb b/bootstraptest/test_eval.rb
index 8e90ac2728..a9f389c673 100644
--- a/bootstraptest/test_eval.rb
+++ b/bootstraptest/test_eval.rb
@@ -116,6 +116,33 @@ assert_equal %q{1}, %q{
Const
}
}
+assert_equal %q{1}, %q{
+ class TrueClass
+ Const = 1
+ end
+ true.instance_eval %{
+ Const
+ }
+}
+assert_equal %q{[:Const]}, %q{
+ mod = Module.new
+ mod.instance_eval %{
+ Const = 1
+ }
+ raise if defined?(Module::Const)
+ mod.singleton_class.constants
+}
+assert_equal %q{can't define singleton}, %q{
+ begin
+ 123.instance_eval %{
+ Const = 1
+ }
+ "bad"
+ rescue TypeError => e
+ raise "bad" if defined?(Integer::Const)
+ e.message
+ end
+}
assert_equal %q{top}, %q{
Const = :top
class C
@@ -190,7 +217,8 @@ assert_equal %q{[10, main]}, %q{
}
%w[break next redo].each do |keyword|
- assert_match %r"Can't escape from eval with #{keyword}\z", %{
+ assert_match %r"Can't escape from eval with #{keyword}\b", %{
+ $stderr = STDOUT
begin
eval "0 rescue #{keyword}"
rescue SyntaxError => e
@@ -200,7 +228,7 @@ assert_equal %q{[10, main]}, %q{
end
assert_normal_exit %q{
- STDERR.reopen(STDOUT)
+ $stderr = STDOUT
class Foo
def self.add_method
class_eval("def some-bad-name; puts 'hello' unless @some_variable.some_function(''); end")
@@ -249,7 +277,9 @@ assert_equal 'ok', %q{
assert_equal 'ok', %q{
begin
- 12.instance_eval { @@a }
+ class A
+ 12.instance_eval { @@a }
+ end
rescue NameError
:ok
end
@@ -257,7 +287,9 @@ assert_equal 'ok', %q{
assert_equal 'ok', %q{
begin
- 12.instance_exec { @@a }
+ class A
+ 12.instance_exec { @@a }
+ end
rescue NameError
:ok
end
diff --git a/bootstraptest/test_fiber.rb b/bootstraptest/test_fiber.rb
new file mode 100644
index 0000000000..2614dd13bf
--- /dev/null
+++ b/bootstraptest/test_fiber.rb
@@ -0,0 +1,39 @@
+show_limit %q{
+ fibers = []
+ begin
+ fiber = Fiber.new{Fiber.yield}
+ fiber.resume
+ fibers << fiber
+
+ raise Exception, "skipping" if fibers.count >= 10_000
+ rescue Exception => error
+ puts "Fiber count: #{fibers.count} (#{error})"
+ break
+ end while true
+}
+
+assert_equal %q{ok}, %q{
+ Fiber.new{
+ }.resume
+ :ok
+}
+
+assert_equal %q{ok}, %q{
+ 100.times.collect{Fiber.new{}}
+ :ok
+}
+
+assert_equal %q{ok}, %q{
+ fibers = 1000.times.collect{Fiber.new{Fiber.yield}}
+ fibers.each(&:resume)
+ fibers.each(&:resume)
+ :ok
+}
+
+assert_normal_exit %q{
+ at_exit { Fiber.new{}.resume }
+}
+
+assert_normal_exit %q{
+ Fiber.new(&Object.method(:class_eval)).resume("foo")
+}, '[ruby-dev:34128]'
diff --git a/bootstraptest/test_flow.rb b/bootstraptest/test_flow.rb
index 9da6d45cbd..35f19db588 100644
--- a/bootstraptest/test_flow.rb
+++ b/bootstraptest/test_flow.rb
@@ -534,11 +534,11 @@ assert_equal %Q{ENSURE\n}, %q{
['[ruby-core:39125]', %q{
class Bug5234
include Enumerable
- def each
+ def each(&block)
begin
yield :foo
ensure
- proc
+ proc(&block)
end
end
end
@@ -547,11 +547,11 @@ assert_equal %Q{ENSURE\n}, %q{
['[ruby-dev:45656]', %q{
class Bug6460
include Enumerable
- def each
+ def each(&block)
begin
yield :foo
ensure
- 1.times { Proc.new }
+ 1.times { Proc.new(&block) }
end
end
end
diff --git a/bootstraptest/test_insns.rb b/bootstraptest/test_insns.rb
index 6de5c60999..91fba9b011 100644
--- a/bootstraptest/test_insns.rb
+++ b/bootstraptest/test_insns.rb
@@ -10,8 +10,10 @@ begin
rescue LoadError
# OK, just skip
else
- $FIXNUM_MAX = RbConfig::LIMITS["FIXNUM_MAX"]
- $FIXNUM_MIN = RbConfig::LIMITS["FIXNUM_MIN"]
+ if defined? RbConfig::LIMITS
+ $FIXNUM_MAX = RbConfig::LIMITS["FIXNUM_MAX"]
+ $FIXNUM_MIN = RbConfig::LIMITS["FIXNUM_MIN"]
+ end
end
fsl = { frozen_string_literal: true } # used later
@@ -26,20 +28,20 @@ tests = [
[ 'getlocal *, 1', %q{ x = true; -> { x }.call }, ],
[ 'getlocal', %q{ x = true; -> { -> { x }.() }.() }, ],
- [ 'setblockparam', <<~'},', ], # {
+ [ 'setblockparam', <<-'},', ], # {
def m&b
b = # here
proc { true }
end
m { false }.call
},
- [ 'getblockparam', <<~'},', ], # {
+ [ 'getblockparam', <<-'},', ], # {
def m&b
b # here
end
m { true }.call
},
- [ 'getblockparamproxy', <<~'},', ], # {
+ [ 'getblockparamproxy', <<-'},', ], # {
def m&b
b # here
.call
@@ -62,8 +64,8 @@ tests = [
[ 'setinstancevariable', %q{ @x = true }, ],
[ 'getinstancevariable', %q{ @x = true; @x }, ],
- [ 'setclassvariable', %q{ @@x = true }, ],
- [ 'getclassvariable', %q{ @@x = true; @@x }, ],
+ [ 'setclassvariable', %q{ class A; @@x = true; end }, ],
+ [ 'getclassvariable', %q{ class A; @@x = true; @@x end }, ],
[ 'setconstant', %q{ X = true }, ],
[ 'setconstant', %q{ Object::X = true }, ],
@@ -84,26 +86,24 @@ tests = [
[ 'putobject', %q{ /(?<x>x)/ =~ "x"; x == "x" }, ],
[ 'putspecialobject', %q{ {//=>true}[//] }, ],
- [ 'putiseq', %q{ -> { true }.() }, ],
[ 'putstring', %q{ "true" }, ],
[ 'tostring / concatstrings', %q{ "#{true}" }, ],
- [ 'freezestring', %q{ "#{true}" }, fsl, ],
- [ 'freezestring', %q{ "#{true}" }, '-d', fsl, ],
[ 'toregexp', %q{ /#{true}/ =~ "true" && $~ }, ],
[ 'intern', %q{ :"#{true}" }, ],
[ 'newarray', %q{ ["true"][0] }, ],
+ [ 'newarraykwsplat', %q{ [**{x:'true'}][0][:x] }, ],
[ 'duparray', %q{ [ true ][0] }, ],
[ 'expandarray', %q{ y = [ true, false, nil ]; x, = y; x }, ],
[ 'expandarray', %q{ y = [ true, false, nil ]; x, *z = y; x }, ],
[ 'expandarray', %q{ y = [ true, false, nil ]; x, *z, w = y; x }, ],
[ 'splatarray', %q{ x, = *(y = true), false; x }, ],
[ 'concatarray', %q{ ["t", "r", *x = "u", "e"].join }, ],
- [ 'concatarray', <<~'},', ], # {
+ [ 'concatarray', <<-'},', ], # {
class X; def to_a; ['u']; end; end
['t', 'r', *X.new, 'e'].join
},
- [ 'concatarray', <<~'},', ], # {
+ [ 'concatarray', <<-'},', ], # {
r = false
t = [true, nil]
q, w, e = r, *t # here
@@ -112,6 +112,7 @@ tests = [
[ 'newhash', %q{ x = {}; x[x] = true }, ],
[ 'newhash', %q{ x = true; { x => x }[x] }, ],
+ [ 'newhashfromarray', %q{ { a: true }[:a] }, ],
[ 'newrange', %q{ x = 1; [*(0..x)][0] == 0 }, ],
[ 'newrange', %q{ x = 1; [*(0...x)][0] == 0 }, ],
@@ -119,7 +120,8 @@ tests = [
[ 'dup', %q{ x = y = true; x }, ],
[ 'dupn', %q{ Object::X ||= true }, ],
[ 'reverse', %q{ q, (w, e), r = 1, [2, 3], 4; e == 3 }, ],
- [ 'swap', <<~'},', ], # {
+ [ 'swap', %q{ !!defined?([[]]) }, ],
+ [ 'swap', <<-'},', ], # {
x = [[false, true]]
for i, j in x # here
;
@@ -134,7 +136,7 @@ tests = [
[ 'defined', %q{ !defined?(x) }, ],
[ 'checkkeyword', %q{ def x x:rand;x end; x x: true }, ],
[ 'checktype', %q{ x = true; "#{x}" }, ],
- [ 'checkmatch', <<~'},', ], # {
+ [ 'checkmatch', <<-'},', ], # {
x = y = true
case x
when false
@@ -144,7 +146,7 @@ tests = [
end
y == nil
},
- [ 'checkmatch', <<~'},', ], # {
+ [ 'checkmatch', <<-'},', ], # {
x, y = true, [false]
case x
when *y # here
@@ -154,7 +156,7 @@ tests = [
end
z
},
- [ 'checkmatch', <<~'},', ], # {
+ [ 'checkmatch', <<-'},', ], # {
x = false
begin
raise
@@ -170,7 +172,7 @@ tests = [
[ 'defineclass', %q{ X = Class.new; class X; true end }, ],
[ 'defineclass', %q{ X = Class.new; class Y < X; true end }, ],
[ 'defineclass', %q{ X = Class.new; class << X; true end }, ],
- [ 'defineclass', <<~'},', ], # {
+ [ 'defineclass', <<-'},', ], # {
X = Class.new
Y = Class.new(X)
class Y < X
@@ -181,7 +183,7 @@ tests = [
[ 'opt_send_without_block', %q{ true.to_s }, ],
[ 'send', %q{ true.tap {|i| i.to_s } }, ],
[ 'leave', %q{ def x; true; end; x }, ],
- [ 'invokesuper', <<~'},', ], # {
+ [ 'invokesuper', <<-'},', ], # {
class X < String
def empty?
super # here
@@ -189,7 +191,7 @@ tests = [
end
X.new.empty?
},
- [ 'invokeblock', <<~'},', ], # {
+ [ 'invokeblock', <<-'},', ], # {
def x
return yield self # here
end
@@ -199,8 +201,11 @@ tests = [
},
[ 'opt_str_freeze', %q{ 'true'.freeze }, ],
+ [ 'opt_nil_p', %q{ nil.nil? }, ],
+ [ 'opt_nil_p', %q{ !Object.nil? }, ],
+ [ 'opt_nil_p', %q{ Class.new{def nil?; true end}.new.nil? }, ],
[ 'opt_str_uminus', %q{ -'true' }, ],
- [ 'opt_str_freeze', <<~'},', ], # {
+ [ 'opt_str_freeze', <<-'},', ], # {
class String
def freeze
true
@@ -211,7 +216,7 @@ tests = [
[ 'opt_newarray_max', %q{ [ ].max.nil? }, ],
[ 'opt_newarray_max', %q{ [1, x = 2, 3].max == 3 }, ],
- [ 'opt_newarray_max', <<~'},', ], # {
+ [ 'opt_newarray_max', <<-'},', ], # {
class Array
def max
true
@@ -221,7 +226,7 @@ tests = [
},
[ 'opt_newarray_min', %q{ [ ].min.nil? }, ],
[ 'opt_newarray_min', %q{ [3, x = 2, 1].min == 1 }, ],
- [ 'opt_newarray_min', <<~'},', ], # {
+ [ 'opt_newarray_min', <<-'},', ], # {
class Array
def min
true
@@ -237,12 +242,12 @@ tests = [
[ 'branchunless', %q{ x = nil; x &&= true; x.nil? }, ],
[ 'branchnil', %q{ x = true; x&.to_s }, ],
[ 'branchnil', %q{ x = nil; (x&.to_s).nil? }, ],
- [ 'jump', <<~'},', ], # {
+ [ 'jump', <<-'},', ], # {
y = 1
x = if y == 0 then nil elsif y == 1 then true else nil end
x
},
- [ 'jump', <<~'},', ], # {
+ [ 'jump', <<-'},', ], # {
# ultra complicated situation: this ||= assignment only generates
# 15 instructions, not including the class definition.
class X; attr_accessor :x; end
@@ -251,14 +256,14 @@ tests = [
},
[ 'once', %q{ /#{true}/o =~ "true" && $~ }, ],
- [ 'once', <<~'},', ], # {
+ [ 'once', <<-'},', ], # {
def once expr
return /#{expr}/o # here
end
x = once(true); x = once(false); x = once(nil);
x =~ "true" && $~
},
- [ 'once', <<~'},', ], # {
+ [ 'once', <<-'},', ], # {
# recursive once
def once n
return %r/#{
@@ -272,7 +277,7 @@ tests = [
x = once(128); x = once(7); x = once(16);
x =~ "true" && $~
},
- [ 'once', <<~'},', ], # {
+ [ 'once', <<-'},', ], # {
# inter-thread lockup situation
def once n
return Thread.start n do |m|
@@ -320,12 +325,12 @@ tests = [
[ 'opt_mod', %q{ '%s' % [ true ] }, ],
[ 'opt_eq', %q{ 1 == 1 }, ],
- [ 'opt_eq', <<~'},', ], # {
+ [ 'opt_eq', <<-'},', ], # {
class X; def == other; true; end; end
X.new == true
},
[ 'opt_neq', %q{ 1 != 0 }, ],
- [ 'opt_neq', <<~'},', ], # {
+ [ 'opt_neq', <<-'},', ], # {
class X; def != other; true; end; end
X.new != true
},
@@ -357,7 +362,7 @@ tests = [
[ 'opt_aset', %q{ [][0] = true }, ],
[ 'opt_aset', %q{ {}[0] = true }, ],
[ 'opt_aset', %q{ x = 'frue'; x[0] = 't'; x }, ],
- [ 'opt_aset', <<~'},', ], # {
+ [ 'opt_aset', <<-'},', ], # {
# opt_aref / opt_aset mixup situation
class X; def x; {}; end; end
x = X.new
@@ -380,40 +385,56 @@ tests = [
[ 'opt_empty_p', %q{ ''.empty? }, ],
[ 'opt_empty_p', %q{ [].empty? }, ],
[ 'opt_empty_p', %q{ {}.empty? }, ],
- [ 'opt_empty_p', %q{ Queue.new.empty? }, ],
+ [ 'opt_empty_p', %q{ Thread::Queue.new.empty? }, ],
[ 'opt_succ', %q{ 1.succ == 2 }, ],
if defined? $FIXNUM_MAX then
[ 'opt_succ',%Q{ #{ $FIXNUM_MAX }.succ == #{ $FIXNUM_MAX + 1 } }, ]
end,
[ 'opt_succ', %q{ '1'.succ == '2' }, ],
- [ 'opt_succ', %q{ x = Time.at(0); x.succ == Time.at(1) }, ],
[ 'opt_not', %q{ ! false }, ],
- [ 'opt_neq', <<~'},', ], # {
+ [ 'opt_neq', <<-'},', ], # {
class X; def !; true; end; end
! X.new
},
- [ 'opt_regexpmatch1', %q{ /true/ =~ 'true' && $~ }, ],
- [ 'opt_regexpmatch1', <<~'},', ], # {
+ [ 'opt_regexpmatch2', %q{ /true/ =~ 'true' && $~ }, ],
+ [ 'opt_regexpmatch2', <<-'},', ], # {
class Regexp; def =~ other; true; end; end
/true/ =~ 'true'
},
[ 'opt_regexpmatch2', %q{ 'true' =~ /true/ && $~ }, ],
- [ 'opt_regexpmatch2', <<~'},', ], # {
+ [ 'opt_regexpmatch2', <<-'},', ], # {
class String; def =~ other; true; end; end
'true' =~ /true/
},
-
- [ 'opt_call_c_function', 'Struct.new(:x).new.x = true', ],
]
# normal path
-tests.compact.each {|(insn, expr, *a)| assert_equal 'true', expr, insn, *a }
+tests.compact.each do |(insn, expr, *a)|
+ if a.last.is_a?(Hash)
+ a = a.dup
+ kw = a.pop
+ assert_equal 'true', expr, insn, *a, **kw
+ else
+ assert_equal 'true', expr, insn, *a
+ end
+end
# with trace
tests.compact.each {|(insn, expr, *a)|
progn = "set_trace_func(proc{})\n" + expr
- assert_equal 'true', progn, insn, *a
+ if a.last.is_a?(Hash)
+ a = a.dup
+ kw = a.pop
+ assert_equal 'true', progn, 'trace_' + insn, *a, **kw
+ else
+ assert_equal 'true', progn, 'trace_' + insn, *a
+ end
}
+
+assert_normal_exit("#{<<-"begin;"}\n#{<<-'end;'}")
+begin;
+ RubyVM::InstructionSequence.compile("", debug_level: 5)
+end;
diff --git a/bootstraptest/test_io.rb b/bootstraptest/test_io.rb
index 89c00d0b88..666e5a011b 100644
--- a/bootstraptest/test_io.rb
+++ b/bootstraptest/test_io.rb
@@ -1,3 +1,4 @@
+/freebsd/ =~ RUBY_PLATFORM or
assert_finish 5, %q{
r, w = IO.pipe
t1 = Thread.new { r.sysread(1) }
@@ -30,7 +31,8 @@ assert_finish 10, %q{
end
}, '[ruby-dev:32566]'
-assert_finish 1, %q{
+/freebsd/ =~ RUBY_PLATFORM or
+assert_finish 5, %q{
r, w = IO.pipe
Thread.new {
w << "ab"
@@ -83,6 +85,7 @@ assert_normal_exit %q{
ARGF.set_encoding "foo"
}
+/freebsd/ =~ RUBY_PLATFORM or
10.times do
assert_normal_exit %q{
at_exit { p :foo }
diff --git a/bootstraptest/test_jump.rb b/bootstraptest/test_jump.rb
index 595aaa7c4b..d07c47a56d 100644
--- a/bootstraptest/test_jump.rb
+++ b/bootstraptest/test_jump.rb
@@ -146,15 +146,16 @@ assert_equal %q{131}, %q{
end
}
}
-assert_equal %q{ok}, %q{
+assert_match %r{Invalid retry}, %q{
+$stderr = STDOUT
begin
eval %q{
1.times{
retry
}
}
-rescue SyntaxError
- :ok
+rescue SyntaxError => e
+ e.message
end
}
assert_equal %q{3}, %q{
@@ -295,14 +296,19 @@ assert_equal "true", %q{
s.return_eigenclass == class << s; self; end
}, '[ruby-core:21379]'
-assert_equal "true", %q{
- class Object
- def yield_eigenclass
- class << self
- yield self
+assert_match %r{Invalid yield}, %q{
+$stderr = STDOUT
+begin
+ eval %q{
+ class Object
+ def yield_eigenclass
+ class << self
+ yield self
+ end
end
end
- end
- s = "foo"
- s.yield_eigenclass {|c| c == class << s; self; end }
-}, '[ruby-dev:40975]'
+ }
+rescue SyntaxError => e
+ e.message
+end
+}
diff --git a/bootstraptest/test_literal.rb b/bootstraptest/test_literal.rb
index 0c5102c46e..a0d4ee08c6 100644
--- a/bootstraptest/test_literal.rb
+++ b/bootstraptest/test_literal.rb
@@ -65,8 +65,11 @@ assert_equal ':a3c', ':"a#{1+2}c".inspect'
assert_equal 'Symbol', ':"a#{1+2}c".class'
# xstring
-assert_equal "foo\n", %q(`echo foo`)
-assert_equal "foo\n", %q(s = "foo"; `echo #{s}`)
+# WASI doesn't support spawning a new process for now.
+if /wasi/ !~ target_platform
+ assert_equal "foo\n", %q(`echo foo`)
+ assert_equal "foo\n", %q(s = "foo"; `echo #{s}`)
+end
# regexp
assert_equal '', '//.source'
@@ -223,6 +226,24 @@ assert_equal 'ok', %q{ # long hash literal (optimized)
:ok
}
+assert_equal 'ok', %q{ # Bug #15536
+ eval <<-END
+ {
+ **{
+ a0: nil, a1: nil, a2: nil, a3: nil, a4: nil, a5: nil, a6: nil, a7: nil, a8: nil,
+ },
+ a0: nil, a1: nil, a2: nil, a3: nil, a4: nil, a5: nil, a6: nil, a7: nil, a8: nil,
+ **{
+ c: nil
+ },
+ b0: nil, b1: nil, b2: nil, b3: nil, b4: nil, b5: nil, b6: nil, b7: nil, b8: nil,
+ b9: nil, b10: nil, b11: nil, b12: nil, b13: nil, b14: nil, b15: nil, b16: nil,
+ b17: nil, b18: nil, b19: nil, b20: nil, b21: nil,
+ }
+ END
+ :ok
+}
+
assert_equal 'ok', %q{
[print(:ok), exit] # void literal with side-effect
:dummy
diff --git a/bootstraptest/test_literal_suffix.rb b/bootstraptest/test_literal_suffix.rb
index 4316c9e040..c36fa7078f 100644
--- a/bootstraptest/test_literal_suffix.rb
+++ b/bootstraptest/test_literal_suffix.rb
@@ -46,9 +46,9 @@ assert_equal '1', '1rescue nil'
assert_equal '10000000000000000001/10000000000000000000',
'1.0000000000000000001r'
-assert_equal 'syntax error, unexpected tIDENTIFIER, expecting end-of-input',
+assert_equal 'syntax error, unexpected local variable or method, expecting end-of-input',
%q{begin eval('1ir', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end}
-assert_equal 'syntax error, unexpected tIDENTIFIER, expecting end-of-input',
+assert_equal 'syntax error, unexpected local variable or method, expecting end-of-input',
%q{begin eval('1.2ir', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end}
-assert_equal 'syntax error, unexpected tIDENTIFIER, expecting end-of-input',
+assert_equal 'syntax error, unexpected local variable or method, expecting end-of-input',
%q{begin eval('1e1r', nil, '', 0); rescue SyntaxError => e; e.message[/\A:(?:\d+:)? (.*)/, 1] end}
diff --git a/bootstraptest/test_method.rb b/bootstraptest/test_method.rb
index 3462aa9434..04c9eb2d11 100644
--- a/bootstraptest/test_method.rb
+++ b/bootstraptest/test_method.rb
@@ -22,7 +22,7 @@ assert_match /\Awrong number of arguments \(.*\b0\b.* 1\)\z/, %q{
}
# default argument
-assert_equal '1', 'def m(x=1) x end; m()'
+assert_equal '1', 'def m(x=1) x end; m();'
assert_equal '1', 'def m(x=7) x end; m(1)'
assert_equal '1', 'def m(a,x=1) x end; m(7)'
assert_equal '1', 'def m(a,x=7) x end; m(7,1)'
diff --git a/bootstraptest/test_objectspace.rb b/bootstraptest/test_objectspace.rb
index 24a1a0ce2c..63a8d99322 100644
--- a/bootstraptest/test_objectspace.rb
+++ b/bootstraptest/test_objectspace.rb
@@ -44,3 +44,12 @@ assert_normal_exit %q{
Thread.new {}
end
}, '[ruby-core:37858]'
+
+assert_equal 'ok', %q{
+ objects_and_ids = 1000.times.map { o = Object.new; [o, o.object_id] }
+ objects_and_ids.each { |expected, id|
+ actual = ObjectSpace._id2ref(id)
+ raise "expected #{expected.inspect}, got #{actual.inspect}" unless actual.equal?(expected)
+ }
+ 'ok'
+}
diff --git a/bootstraptest/test_proc.rb b/bootstraptest/test_proc.rb
index 1e384411dc..637603243d 100644
--- a/bootstraptest/test_proc.rb
+++ b/bootstraptest/test_proc.rb
@@ -225,19 +225,6 @@ assert_equal %q{[[nil, []], [1, []], [1, [2]], [1, [2, 3]]]}, %q{
]
}
assert_equal %q{1}, %q{
- pr = proc{
- $SAFE
- }
- $SAFE = 1
- pr.call
-}
-assert_equal %q{[1, 1]}, %q{
- pr = proc{
- $SAFE += 1
- }
- [pr.call, $SAFE]
-}
-assert_equal %q{1}, %q{
def m(&b)
b
end
@@ -380,8 +367,8 @@ assert_equal 'ok', %q{
assert_equal 'ok', %q{
class Foo
- def call_it
- p = Proc.new
+ def call_it(&block)
+ p = Proc.new(&block)
p.call
end
end
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb
new file mode 100644
index 0000000000..67e66b03ee
--- /dev/null
+++ b/bootstraptest/test_ractor.rb
@@ -0,0 +1,1628 @@
+# Ractor.current returns a current ractor
+assert_equal 'Ractor', %q{
+ Ractor.current.class
+}
+
+# Ractor.new returns new Ractor
+assert_equal 'Ractor', %q{
+ Ractor.new{}.class
+}
+
+# Ractor.allocate is not supported
+assert_equal "[:ok, :ok]", %q{
+ rs = []
+ begin
+ Ractor.allocate
+ rescue => e
+ rs << :ok if e.message == 'allocator undefined for Ractor'
+ end
+
+ begin
+ Ractor.new{}.dup
+ rescue
+ rs << :ok if e.message == 'allocator undefined for Ractor'
+ end
+
+ rs
+}
+
+# A Ractor can have a name
+assert_equal 'test-name', %q{
+ r = Ractor.new name: 'test-name' do
+ end
+ r.name
+}
+
+# If Ractor doesn't have a name, Ractor#name returns nil.
+assert_equal 'nil', %q{
+ r = Ractor.new do
+ end
+ r.name.inspect
+}
+
+# Raises exceptions if initialize with an invalid name
+assert_equal 'ok', %q{
+ begin
+ r = Ractor.new(name: [{}]) {}
+ rescue TypeError => e
+ 'ok'
+ end
+}
+
+# Ractor.new must call with a block
+assert_equal "must be called with a block", %q{
+ begin
+ Ractor.new
+ rescue ArgumentError => e
+ e.message
+ end
+}
+
+# Ractor#inspect
+# Return only id and status for main ractor
+assert_equal "#<Ractor:#1 running>", %q{
+ Ractor.current.inspect
+}
+
+# Return id, loc, and status for no-name ractor
+assert_match /^#<Ractor:#([^ ]*?) .+:[0-9]+ terminated>$/, %q{
+ r = Ractor.new { '' }
+ r.take
+ sleep 0.1 until r.inspect =~ /terminated/
+ r.inspect
+}
+
+# Return id, name, loc, and status for named ractor
+assert_match /^#<Ractor:#([^ ]*?) Test Ractor .+:[0-9]+ terminated>$/, %q{
+ r = Ractor.new(name: 'Test Ractor') { '' }
+ r.take
+ sleep 0.1 until r.inspect =~ /terminated/
+ r.inspect
+}
+
+# A return value of a Ractor block will be a message from the Ractor.
+assert_equal 'ok', %q{
+ # join
+ r = Ractor.new do
+ 'ok'
+ end
+ r.take
+}
+
+# Passed arguments to Ractor.new will be a block parameter
+# The values are passed with Ractor-communication pass.
+assert_equal 'ok', %q{
+ # ping-pong with arg
+ r = Ractor.new 'ok' do |msg|
+ msg
+ end
+ r.take
+}
+
+# Pass multiple arguments to Ractor.new
+assert_equal 'ok', %q{
+ # ping-pong with two args
+ r = Ractor.new 'ping', 'pong' do |msg, msg2|
+ [msg, msg2]
+ end
+ 'ok' if r.take == ['ping', 'pong']
+}
+
+# Ractor#send passes an object with copy to a Ractor
+# and Ractor.receive in the Ractor block can receive the passed value.
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ msg = Ractor.receive
+ end
+ r.send 'ok'
+ r.take
+}
+
+# Ractor#receive_if can filter the message
+assert_equal '[2, 3, 1]', %q{
+ r = Ractor.new Ractor.current do |main|
+ main << 1
+ main << 2
+ main << 3
+ end
+ a = []
+ a << Ractor.receive_if{|msg| msg == 2}
+ a << Ractor.receive_if{|msg| msg == 3}
+ a << Ractor.receive
+}
+
+# Ractor#receive_if with break
+assert_equal '[2, [1, :break], 3]', %q{
+ r = Ractor.new Ractor.current do |main|
+ main << 1
+ main << 2
+ main << 3
+ end
+
+ a = []
+ a << Ractor.receive_if{|msg| msg == 2}
+ a << Ractor.receive_if{|msg| break [msg, :break]}
+ a << Ractor.receive
+}
+
+# Ractor#receive_if can't be called recursively
+assert_equal '[[:e1, 1], [:e2, 2]]', %q{
+ r = Ractor.new Ractor.current do |main|
+ main << 1
+ main << 2
+ main << 3
+ end
+
+ a = []
+
+ Ractor.receive_if do |msg|
+ begin
+ Ractor.receive
+ rescue Ractor::Error
+ a << [:e1, msg]
+ end
+ true # delete 1 from queue
+ end
+
+ Ractor.receive_if do |msg|
+ begin
+ Ractor.receive_if{}
+ rescue Ractor::Error
+ a << [:e2, msg]
+ end
+ true # delete 2 from queue
+ end
+
+ a #
+}
+
+# dtoa race condition
+assert_equal '[:ok, :ok, :ok]', %q{
+ n = 3
+ n.times.map{
+ Ractor.new{
+ 10_000.times{ rand.to_s }
+ :ok
+ }
+ }.map(&:take)
+}
+
+# Ractor.make_shareable issue for locals in proc [Bug #18023]
+assert_equal '[:a, :b, :c, :d, :e]', %q{
+ v1, v2, v3, v4, v5 = :a, :b, :c, :d, :e
+ closure = Ractor.current.instance_eval{ Proc.new { [v1, v2, v3, v4, v5] } }
+
+ Ractor.make_shareable(closure).call
+}
+
+# Ractor.make_shareable issue for locals in proc [Bug #18023]
+assert_equal '[:a, :b, :c, :d, :e, :f, :g]', %q{
+ a = :a
+ closure = Ractor.current.instance_eval do
+ -> {
+ b, c, d = :b, :c, :d
+ -> {
+ e, f, g = :e, :f, :g
+ -> { [a, b, c, d, e, f, g] }
+ }.call
+ }.call
+ end
+
+ Ractor.make_shareable(closure).call
+}
+
+# Now autoload in non-main Ractor is not supported
+assert_equal 'ok', %q{
+ autoload :Foo, 'foo.rb'
+ r = Ractor.new do
+ p Foo
+ rescue Ractor::UnsafeError
+ :ok
+ end
+ r.take
+}
+
+###
+###
+# Ractor still has several memory corruption so skip huge number of tests
+if ENV['GITHUB_WORKFLOW'] &&
+ ENV['GITHUB_WORKFLOW'] == 'Compilations'
+ # ignore the follow
+else
+
+# Ractor.select(*ractors) receives a values from a ractors.
+# It is similar to select(2) and Go's select syntax.
+# The return value is [ch, received_value]
+assert_equal 'ok', %q{
+ # select 1
+ r1 = Ractor.new{'r1'}
+ r, obj = Ractor.select(r1)
+ 'ok' if r == r1 and obj == 'r1'
+}
+
+# Ractor.select from two ractors.
+assert_equal '["r1", "r2"]', %q{
+ # select 2
+ r1 = Ractor.new{'r1'}
+ r2 = Ractor.new{'r2'}
+ rs = [r1, r2]
+ as = []
+ r, obj = Ractor.select(*rs)
+ rs.delete(r)
+ as << obj
+ r, obj = Ractor.select(*rs)
+ as << obj
+ as.sort #=> ["r1", "r2"]
+}
+
+# Ractor.select from multiple ractors.
+assert_equal 30.times.map { 'ok' }.to_s, %q{
+ def test n
+ rs = (1..n).map do |i|
+ Ractor.new(i) do |i|
+ "r#{i}"
+ end
+ end
+ as = []
+ all_rs = rs.dup
+
+ n.times{
+ r, obj = Ractor.select(*rs)
+ as << [r, obj]
+ rs.delete(r)
+ }
+
+ if as.map{|r, o| r.object_id}.sort == all_rs.map{|r| r.object_id}.sort &&
+ as.map{|r, o| o}.sort == (1..n).map{|i| "r#{i}"}.sort
+ 'ok'
+ else
+ 'ng'
+ end
+ end
+
+ 30.times.map{|i|
+ test i
+ }
+} unless ENV['RUN_OPTS'] =~ /--mjit-call-threshold=5/ || # This always fails with --mjit-wait --mjit-call-threshold=5
+ (ENV.key?('TRAVIS') && ENV['TRAVIS_CPU_ARCH'] == 'arm64') || # https://bugs.ruby-lang.org/issues/17878
+ true # too flaky everywhere http://ci.rvm.jp/results/trunk@ruby-sp1/4321096
+
+# Exception for empty select
+assert_match /specify at least one ractor/, %q{
+ begin
+ Ractor.select
+ rescue ArgumentError => e
+ e.message
+ end
+}
+
+# Outgoing port of a ractor will be closed when the Ractor is terminated.
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ 'finish'
+ end
+
+ r.take
+ sleep 0.1 until r.inspect =~ /terminated/
+
+ begin
+ o = r.take
+ rescue Ractor::ClosedError
+ 'ok'
+ else
+ "ng: #{o}"
+ end
+}
+
+# Raise Ractor::ClosedError when try to send into a terminated ractor
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ end
+
+ r.take # closed
+ sleep 0.1 until r.inspect =~ /terminated/
+
+ begin
+ r.send(1)
+ rescue Ractor::ClosedError
+ 'ok'
+ else
+ 'ng'
+ end
+}
+
+# Raise Ractor::ClosedError when try to send into a closed actor
+assert_equal 'ok', %q{
+ r = Ractor.new { Ractor.receive }
+ r.close_incoming
+
+ begin
+ r.send(1)
+ rescue Ractor::ClosedError
+ 'ok'
+ else
+ 'ng'
+ end
+}
+
+# Raise Ractor::ClosedError when try to take from closed actor
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ Ractor.yield 1
+ Ractor.receive
+ end
+
+ r.close_outgoing
+ begin
+ r.take
+ rescue Ractor::ClosedError
+ 'ok'
+ else
+ 'ng'
+ end
+}
+
+# Can mix with Thread#interrupt and Ractor#take [Bug #17366]
+assert_equal 'err', %q{
+ Ractor.new{
+ t = Thread.current
+ begin
+ Thread.new{ t.raise "err" }.join
+ rescue => e
+ e.message
+ end
+ }.take
+}
+
+# Killed Ractor's thread yields nil
+assert_equal 'nil', %q{
+ Ractor.new{
+ t = Thread.current
+ Thread.new{ t.kill }.join
+ }.take.inspect #=> nil
+}
+
+# Ractor.yield raises Ractor::ClosedError when outgoing port is closed.
+assert_equal 'ok', %q{
+ r = Ractor.new Ractor.current do |main|
+ Ractor.receive
+ main << true
+ Ractor.yield 1
+ end
+
+ r.close_outgoing
+ r << true
+ Ractor.receive
+
+ begin
+ r.take
+ rescue Ractor::ClosedError
+ 'ok'
+ else
+ 'ng'
+ end
+}
+
+# Raise Ractor::ClosedError when try to send into a ractor with closed incoming port
+assert_equal 'ok', %q{
+ r = Ractor.new { Ractor.receive }
+ r.close_incoming
+
+ begin
+ r.send(1)
+ rescue Ractor::ClosedError
+ 'ok'
+ else
+ 'ng'
+ end
+}
+
+# A ractor with closed incoming port still can send messages out
+assert_equal '[1, 2]', %q{
+ r = Ractor.new do
+ Ractor.yield 1
+ 2
+ end
+ r.close_incoming
+
+ [r.take, r.take]
+}
+
+# Raise Ractor::ClosedError when try to take from a ractor with closed outgoing port
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ Ractor.yield 1
+ Ractor.receive
+ end
+
+ sleep 0.01 # wait for Ractor.yield in r
+ r.close_outgoing
+ begin
+ r.take
+ rescue Ractor::ClosedError
+ 'ok'
+ else
+ 'ng'
+ end
+}
+
+# A ractor with closed outgoing port still can receive messages from incoming port
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ Ractor.receive
+ end
+
+ r.close_outgoing
+ begin
+ r.send(1)
+ rescue Ractor::ClosedError
+ 'ng'
+ else
+ 'ok'
+ end
+}
+
+# Ractor.main returns main ractor
+assert_equal 'true', %q{
+ Ractor.new{
+ Ractor.main
+ }.take == Ractor.current
+}
+
+# a ractor with closed outgoing port should terminate
+assert_equal 'ok', %q{
+ Ractor.new do
+ close_outgoing
+ end
+
+ true until Ractor.count == 1
+ :ok
+}
+
+# multiple Ractors can receive (wait) from one Ractor
+assert_equal '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]', %q{
+ pipe = Ractor.new do
+ loop do
+ Ractor.yield Ractor.receive
+ end
+ end
+
+ RN = 10
+ rs = RN.times.map{|i|
+ Ractor.new pipe, i do |pipe, i|
+ msg = pipe.take
+ msg # ping-pong
+ end
+ }
+ RN.times{|i|
+ pipe << i
+ }
+ RN.times.map{
+ r, n = Ractor.select(*rs)
+ rs.delete r
+ n
+ }.sort
+} unless /mswin/ =~ RUBY_PLATFORM # randomly hangs on mswin https://github.com/ruby/ruby/actions/runs/3753871445/jobs/6377551069#step:20:131
+
+# Ractor.select also support multiple take, receive and yield
+assert_equal '[true, true, true]', %q{
+ RN = 10
+ CR = Ractor.current
+
+ rs = (1..RN).map{
+ Ractor.new do
+ CR.send 'send' + CR.take #=> 'sendyield'
+ 'take'
+ end
+ }
+ received = []
+ take = []
+ yielded = []
+ until rs.empty?
+ r, v = Ractor.select(CR, *rs, yield_value: 'yield')
+ case r
+ when :receive
+ received << v
+ when :yield
+ yielded << v
+ else
+ take << v
+ rs.delete r
+ end
+ end
+ [received.all?('sendyield'), yielded.all?(nil), take.all?('take')]
+}
+
+# multiple Ractors can send to one Ractor
+assert_equal '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]', %q{
+ pipe = Ractor.new do
+ loop do
+ Ractor.yield Ractor.receive
+ end
+ end
+
+ RN = 10
+ RN.times.map{|i|
+ Ractor.new pipe, i do |pipe, i|
+ pipe << i
+ end
+ }
+ RN.times.map{
+ pipe.take
+ }.sort
+}
+
+# an exception in a Ractor will be re-raised at Ractor#receive
+assert_equal '[RuntimeError, "ok", true]', %q{
+ r = Ractor.new do
+ raise 'ok' # exception will be transferred receiver
+ end
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ [e.cause.class, #=> RuntimeError
+ e.cause.message, #=> 'ok'
+ e.ractor == r] #=> true
+ end
+}
+
+# threads in a ractor will killed
+assert_equal '{:ok=>3}', %q{
+ Ractor.new Ractor.current do |main|
+ q = Thread::Queue.new
+ Thread.new do
+ q << true
+ loop{}
+ ensure
+ main << :ok
+ end
+
+ Thread.new do
+ q << true
+ while true
+ end
+ ensure
+ main << :ok
+ end
+
+ Thread.new do
+ q << true
+ sleep 1
+ ensure
+ main << :ok
+ end
+
+ # wait for the start of all threads
+ 3.times{q.pop}
+ end
+
+ 3.times.map{Ractor.receive}.tally
+}
+
+# unshareable object are copied
+assert_equal 'false', %q{
+ obj = 'str'.dup
+ r = Ractor.new obj do |msg|
+ msg.object_id
+ end
+
+ obj.object_id == r.take
+}
+
+# To copy the object, now Marshal#dump is used
+assert_equal "allocator undefined for Thread", %q{
+ obj = Thread.new{}
+ begin
+ r = Ractor.new obj do |msg|
+ msg
+ end
+ rescue TypeError => e
+ e.message #=> no _dump_data is defined for class Thread
+ else
+ 'ng'
+ end
+}
+
+# send shareable and unshareable objects
+assert_equal "ok", %q{
+ echo_ractor = Ractor.new do
+ loop do
+ v = Ractor.receive
+ Ractor.yield v
+ end
+ end
+
+ class C; end
+ module M; end
+ S = Struct.new(:a, :b, :c, :d)
+
+ shareable_objects = [
+ true,
+ false,
+ nil,
+ 1,
+ 1.1, # Float
+ 1+2r, # Rational
+ 3+4i, # Complex
+ 2**128, # Bignum
+ :sym, # Symbol
+ 'xyzzy'.to_sym, # dynamic symbol
+ 'frozen'.freeze, # frozen String
+ /regexp/, # regexp literal
+ /reg{true}exp/.freeze, # frozen dregexp
+ [1, 2].freeze, # frozen Array which only refers to shareable
+ {a: 1}.freeze, # frozen Hash which only refers to shareable
+ [{a: 1}.freeze, 'str'.freeze].freeze, # nested frozen container
+ S.new(1, 2).freeze, # frozen Struct
+ S.new(1, 2, 3, 4).freeze, # frozen Struct
+ (1..2), # Range on Struct
+ (1..), # Range on Struct
+ (..1), # Range on Struct
+ C, # class
+ M, # module
+ Ractor.current, # Ractor
+ ]
+
+ unshareable_objects = [
+ 'mutable str'.dup,
+ [:array],
+ {hash: true},
+ S.new(1, 2),
+ S.new(1, 2, 3, 4),
+ S.new("a", 2).freeze, # frozen, but refers to an unshareable object
+ ]
+
+ results = []
+
+ shareable_objects.map{|o|
+ echo_ractor << o
+ o2 = echo_ractor.take
+ results << "#{o} is copied" unless o.object_id == o2.object_id
+ }
+
+ unshareable_objects.map{|o|
+ echo_ractor << o
+ o2 = echo_ractor.take
+ results << "#{o.inspect} is not copied" if o.object_id == o2.object_id
+ }
+
+ if results.empty?
+ :ok
+ else
+ results.inspect
+ end
+}
+
+# frozen Objects are shareable
+assert_equal [false, true, false].inspect, %q{
+ class C
+ def initialize freeze
+ @a = 1
+ @b = :sym
+ @c = 'frozen_str'
+ @c.freeze if freeze
+ @d = true
+ end
+ end
+
+ def check obj1
+ obj2 = Ractor.new obj1 do |obj|
+ obj
+ end.take
+
+ obj1.object_id == obj2.object_id
+ end
+
+ results = []
+ results << check(C.new(true)) # false
+ results << check(C.new(true).freeze) # true
+ results << check(C.new(false).freeze) # false
+}
+
+# move example2: String
+# touching moved object causes an error
+assert_equal 'hello world', %q{
+ # move
+ r = Ractor.new do
+ obj = Ractor.receive
+ obj << ' world'
+ end
+
+ str = 'hello'
+ r.send str, move: true
+ modified = r.take
+
+ begin
+ str << ' exception' # raise Ractor::MovedError
+ rescue Ractor::MovedError
+ modified #=> 'hello world'
+ else
+ raise 'unreachable'
+ end
+}
+
+# move example2: Array
+assert_equal '[0, 1]', %q{
+ r = Ractor.new do
+ ary = Ractor.receive
+ ary << 1
+ end
+
+ a1 = [0]
+ r.send a1, move: true
+ a2 = r.take
+ begin
+ a1 << 2 # raise Ractor::MovedError
+ rescue Ractor::MovedError
+ a2.inspect
+ end
+}
+
+# move with yield
+assert_equal 'hello', %q{
+ r = Ractor.new do
+ Thread.current.report_on_exception = false
+ obj = 'hello'
+ Ractor.yield obj, move: true
+ obj << 'world'
+ end
+
+ str = r.take
+ begin
+ r.take
+ rescue Ractor::RemoteError
+ str #=> "hello"
+ end
+}
+
+# yield/move should not make moved object when the yield is not succeeded
+assert_equal '"str"', %q{
+ R = Ractor.new{}
+ M = Ractor.current
+ r = Ractor.new do
+ s = 'str'
+ selected_r, v = Ractor.select R, yield_value: s, move: true
+ raise if selected_r != R # taken from R
+ M.send s.inspect # s should not be a moved object
+ end
+
+ Ractor.receive
+}
+
+# yield/move can fail
+assert_equal "allocator undefined for Thread", %q{
+ r = Ractor.new do
+ obj = Thread.new{}
+ Ractor.yield obj
+ rescue => e
+ e.message
+ end
+ r.take
+}
+
+# Access to global-variables are prohibited
+assert_equal 'can not access global variables $gv from non-main Ractors', %q{
+ $gv = 1
+ r = Ractor.new do
+ $gv
+ end
+
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# Access to global-variables are prohibited
+assert_equal 'can not access global variables $gv from non-main Ractors', %q{
+ r = Ractor.new do
+ $gv = 1
+ end
+
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# $stdin,out,err is Ractor local, but shared fds
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ [$stdin, $stdout, $stderr].map{|io|
+ [io.object_id, io.fileno]
+ }
+ end
+
+ [$stdin, $stdout, $stderr].zip(r.take){|io, (oid, fno)|
+ raise "should not be different object" if io.object_id == oid
+ raise "fd should be same" unless io.fileno == fno
+ }
+ 'ok'
+}
+
+# $stdin,out,err belong to Ractor
+assert_equal 'ok', %q{
+ r = Ractor.new do
+ $stdin.itself
+ $stdout.itself
+ $stderr.itself
+ 'ok'
+ end
+
+ r.take
+}
+
+# $DEBUG, $VERBOSE are Ractor local
+assert_equal 'true', %q{
+ $DEBUG = true
+ $VERBOSE = true
+
+ def ractor_local_globals
+ /a(b)(c)d/ =~ 'abcd' # for $~
+ `echo foo` unless /solaris/ =~ RUBY_PLATFORM
+
+ {
+ # ractor-local (derived from created ractor): debug
+ '$DEBUG' => $DEBUG,
+ '$-d' => $-d,
+
+ # ractor-local (derived from created ractor): verbose
+ '$VERBOSE' => $VERBOSE,
+ '$-w' => $-w,
+ '$-W' => $-W,
+ '$-v' => $-v,
+
+ # process-local (readonly): other commandline parameters
+ '$-p' => $-p,
+ '$-l' => $-l,
+ '$-a' => $-a,
+
+ # process-local (readonly): getpid
+ '$$' => $$,
+
+ # thread local: process result
+ '$?' => $?,
+
+ # scope local: match
+ '$~' => $~.inspect,
+ '$&' => $&,
+ '$`' => $`,
+ '$\'' => $',
+ '$+' => $+,
+ '$1' => $1,
+
+ # scope local: last line
+ '$_' => $_,
+
+ # scope local: last backtrace
+ '$@' => $@,
+ '$!' => $!,
+
+ # ractor local: stdin, out, err
+ '$stdin' => $stdin.inspect,
+ '$stdout' => $stdout.inspect,
+ '$stderr' => $stderr.inspect,
+ }
+ end
+
+ h = Ractor.new do
+ ractor_local_globals
+ end.take
+ ractor_local_globals == h #=> true
+}
+
+# selfs are different objects
+assert_equal 'false', %q{
+ r = Ractor.new do
+ self.object_id
+ end
+ r.take == self.object_id #=> false
+}
+
+# self is a Ractor instance
+assert_equal 'true', %q{
+ r = Ractor.new do
+ self.object_id
+ end
+ r.object_id == r.take #=> true
+}
+
+# given block Proc will be isolated, so can not access outer variables.
+assert_equal 'ArgumentError', %q{
+ begin
+ a = true
+ r = Ractor.new do
+ a
+ end
+ rescue => e
+ e.class
+ end
+}
+
+# ivar in shareable-objects are not allowed to access from non-main Ractor
+assert_equal "can not get unshareable values from instance variables of classes/modules from non-main Ractors", %q{
+ class C
+ @iv = 'str'
+ end
+
+ r = Ractor.new do
+ class C
+ p @iv
+ end
+ end
+
+
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# ivar in shareable-objects are not allowed to access from non-main Ractor
+assert_equal 'can not access instance variables of shareable objects from non-main Ractors', %q{
+ shared = Ractor.new{}
+ shared.instance_variable_set(:@iv, 'str')
+
+ r = Ractor.new shared do |shared|
+ p shared.instance_variable_get(:@iv)
+ end
+
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (get)
+assert_equal 'can not access instance variables of shareable objects from non-main Ractors', %q{
+ class Ractor
+ def setup
+ @foo = ''
+ end
+
+ def foo
+ @foo
+ end
+ end
+
+ shared = Ractor.new{}
+ shared.setup
+
+ r = Ractor.new shared do |shared|
+ p shared.foo
+ end
+
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# ivar in shareable-objects are not allowed to access from non-main Ractor, by @iv (set)
+assert_equal 'can not access instance variables of shareable objects from non-main Ractors', %q{
+ class Ractor
+ def setup
+ @foo = ''
+ end
+ end
+
+ shared = Ractor.new{}
+
+ r = Ractor.new shared do |shared|
+ p shared.setup
+ end
+
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# But a shareable object is frozen, it is allowed to access ivars from non-main Ractor
+assert_equal '11', %q{
+ [Object.new, [], ].map{|obj|
+ obj.instance_variable_set('@a', 1)
+ Ractor.make_shareable obj = obj.freeze
+
+ Ractor.new obj do |obj|
+ obj.instance_variable_get('@a')
+ end.take.to_s
+ }.join
+}
+
+# and instance variables of classes/modules are accessible if they refer shareable objects
+assert_equal '333', %q{
+ class C
+ @int = 1
+ @str = '-1000'.dup
+ @fstr = '100'.freeze
+
+ def self.int = @int
+ def self.str = @str
+ def self.fstr = @fstr
+ end
+
+ module M
+ @int = 2
+ @str = '-2000'.dup
+ @fstr = '200'.freeze
+
+ def self.int = @int
+ def self.str = @str
+ def self.fstr = @fstr
+ end
+
+ a = Ractor.new{ C.int }.take
+ b = Ractor.new do
+ C.str.to_i
+ rescue Ractor::IsolationError
+ 10
+ end.take
+ c = Ractor.new do
+ C.fstr.to_i
+ end.take
+
+ d = Ractor.new{ M.int }.take
+ e = Ractor.new do
+ M.str.to_i
+ rescue Ractor::IsolationError
+ 20
+ end.take
+ f = Ractor.new do
+ M.fstr.to_i
+ end.take
+
+
+ # 1 + 10 + 100 + 2 + 20 + 200
+ a + b + c + d + e + f
+}
+
+# cvar in shareable-objects are not allowed to access from non-main Ractor
+assert_equal 'can not access class variables from non-main Ractors', %q{
+ class C
+ @@cv = 'str'
+ end
+
+ r = Ractor.new do
+ class C
+ p @@cv
+ end
+ end
+
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# also cached cvar in shareable-objects are not allowed to access from non-main Ractor
+assert_equal 'can not access class variables from non-main Ractors', %q{
+ class C
+ @@cv = 'str'
+ def self.cv
+ @@cv
+ end
+ end
+
+ C.cv # cache
+
+ r = Ractor.new do
+ C.cv
+ end
+
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# Getting non-shareable objects via constants by other Ractors is not allowed
+assert_equal 'can not access non-shareable objects in constant C::CONST by non-main Ractor.', %q{
+ class C
+ CONST = 'str'
+ end
+ r = Ractor.new do
+ C::CONST
+ end
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# Constant cache should care about non-sharable constants
+assert_equal "can not access non-shareable objects in constant Object::STR by non-main Ractor.", %q{
+ STR = "hello"
+ def str; STR; end
+ s = str() # fill const cache
+ begin
+ Ractor.new{ str() }.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# Setting non-shareable objects into constants by other Ractors is not allowed
+assert_equal 'can not set constants with non-shareable objects by non-main Ractors', %q{
+ class C
+ end
+ r = Ractor.new do
+ C::CONST = 'str'
+ end
+ begin
+ r.take
+ rescue Ractor::RemoteError => e
+ e.cause.message
+ end
+}
+
+# define_method is not allowed
+assert_equal "defined with an un-shareable Proc in a different Ractor", %q{
+ str = "foo"
+ define_method(:buggy){|i| str << "#{i}"}
+ begin
+ Ractor.new{buggy(10)}.take
+ rescue => e
+ e.cause.message
+ end
+}
+
+# Immutable Array and Hash are shareable, so it can be shared with constants
+assert_equal '[1000, 3]', %q{
+ A = Array.new(1000).freeze # [nil, ...]
+ H = {a: 1, b: 2, c: 3}.freeze
+
+ Ractor.new{ [A.size, H.size] }.take
+}
+
+# Ractor.count
+assert_equal '[1, 4, 3, 2, 1]', %q{
+ counts = []
+ counts << Ractor.count
+ ractors = (1..3).map { Ractor.new { Ractor.receive } }
+ counts << Ractor.count
+
+ ractors[0].send('End 0').take
+ sleep 0.1 until ractors[0].inspect =~ /terminated/
+ counts << Ractor.count
+
+ ractors[1].send('End 1').take
+ sleep 0.1 until ractors[1].inspect =~ /terminated/
+ counts << Ractor.count
+
+ ractors[2].send('End 2').take
+ sleep 0.1 until ractors[2].inspect =~ /terminated/
+ counts << Ractor.count
+
+ counts.inspect
+}
+
+# ObjectSpace.each_object can not handle unshareable objects with Ractors
+assert_equal '0', %q{
+ Ractor.new{
+ n = 0
+ ObjectSpace.each_object{|o| n += 1 unless Ractor.shareable?(o)}
+ n
+ }.take
+}
+
+# ObjectSpace._id2ref can not handle unshareable objects with Ractors
+assert_equal 'ok', %q{
+ s = 'hello'
+
+ Ractor.new s.object_id do |id ;s|
+ begin
+ s = ObjectSpace._id2ref(id)
+ rescue => e
+ :ok
+ end
+ end.take
+}
+
+# Ractor.make_shareable(obj)
+assert_equal 'true', %q{
+ class C
+ def initialize
+ @a = 'foo'
+ @b = 'bar'
+ end
+
+ def freeze
+ @c = [:freeze_called]
+ super
+ end
+
+ attr_reader :a, :b, :c
+ end
+ S = Struct.new(:s1, :s2)
+ str = "hello"
+ str.instance_variable_set("@iv", "hello")
+ /a/ =~ 'a'
+ m = $~
+ class N < Numeric
+ def /(other)
+ 1
+ end
+ end
+ ary = []; ary << ary
+
+ a = [[1, ['2', '3']],
+ {Object.new => "hello"},
+ C.new,
+ S.new("x", "y"),
+ ("a".."b"),
+ str,
+ ary, # cycle
+ /regexp/,
+ /#{'r'.upcase}/,
+ m,
+ Complex(N.new,0),
+ Rational(N.new,0),
+ true,
+ false,
+ nil,
+ 1, 1.2, 1+3r, 1+4i, # Numeric
+ ]
+ Ractor.make_shareable(a)
+
+ # check all frozen
+ a.each{|o|
+ raise o.inspect unless o.frozen?
+
+ case o
+ when C
+ raise o.a.inspect unless o.a.frozen?
+ raise o.b.inspect unless o.b.frozen?
+ raise o.c.inspect unless o.c.frozen? && o.c == [:freeze_called]
+ when Rational
+ raise o.numerator.inspect unless o.numerator.frozen?
+ when Complex
+ raise o.real.inspect unless o.real.frozen?
+ when Array
+ if o[0] == 1
+ raise o[1][1].inspect unless o[1][1].frozen?
+ end
+ when Hash
+ o.each{|k, v|
+ raise k.inspect unless k.frozen?
+ raise v.inspect unless v.frozen?
+ }
+ end
+ }
+
+ Ractor.shareable?(a)
+}
+
+# Ractor.make_shareable(obj) doesn't freeze shareable objects
+assert_equal 'true', %q{
+ r = Ractor.new{}
+ Ractor.make_shareable(a = [r])
+ [a.frozen?, a[0].frozen?] == [true, false]
+}
+
+# Ractor.make_shareable(a_proc) makes a proc shareable.
+assert_equal 'true', %q{
+ a = [1, [2, 3], {a: "4"}]
+
+ pr = Ractor.current.instance_eval do
+ Proc.new do
+ a
+ end
+ end
+
+ Ractor.make_shareable(a) # referred value should be shareable
+ Ractor.make_shareable(pr)
+ Ractor.shareable?(pr)
+}
+
+# Ractor.shareable?(recursive_objects)
+assert_equal '[false, false]', %q{
+ y = []
+ x = [y, {}].freeze
+ y << x
+ y.freeze
+ [Ractor.shareable?(x), Ractor.shareable?(y)]
+}
+
+# Ractor.make_shareable(recursive_objects)
+assert_equal '[:ok, false, false]', %q{
+ o = Object.new
+ def o.freeze; raise; end
+ y = []
+ x = [y, o].freeze
+ y << x
+ y.freeze
+ [(Ractor.make_shareable(x) rescue :ok), Ractor.shareable?(x), Ractor.shareable?(y)]
+}
+
+# Ractor.make_shareable with Class/Module
+assert_equal '[C, M]', %q{
+ class C; end
+ module M; end
+
+ Ractor.make_shareable(ary = [C, M])
+}
+
+# define_method() can invoke different Ractor's proc if the proc is shareable.
+assert_equal '1', %q{
+ class C
+ a = 1
+ define_method "foo", Ractor.make_shareable(Proc.new{ a })
+ a = 2
+ end
+
+ Ractor.new{ C.new.foo }.take
+}
+
+# Ractor.make_shareable(a_proc) makes a proc shareable.
+assert_equal 'can not make a Proc shareable because it accesses outer variables (a).', %q{
+ a = b = nil
+ pr = Ractor.current.instance_eval do
+ Proc.new do
+ c = b # assign to a is okay because c is block local variable
+ # reading b is okay
+ a = b # assign to a is not allowed #=> Ractor::Error
+ end
+ end
+
+ begin
+ Ractor.make_shareable(pr)
+ rescue => e
+ e.message
+ end
+}
+
+# Ractor.make_shareable(obj, copy: true) makes copied shareable object.
+assert_equal '[false, false, true, true]', %q{
+ r = []
+ o1 = [1, 2, ["3"]]
+
+ o2 = Ractor.make_shareable(o1, copy: true)
+ r << Ractor.shareable?(o1) # false
+ r << (o1.object_id == o2.object_id) # false
+
+ o3 = Ractor.make_shareable(o1)
+ r << Ractor.shareable?(o1) # true
+ r << (o1.object_id == o3.object_id) # false
+ r
+}
+
+# TracePoint with normal Proc should be Ractor local
+assert_equal '[4, 8]', %q{
+ rs = []
+ TracePoint.new(:line){|tp| rs << tp.lineno if tp.path == __FILE__}.enable do
+ Ractor.new{ # line 4
+ a = 1
+ b = 2
+ }.take
+ c = 3 # line 8
+ end
+ rs
+}
+
+# Ractor deep copies frozen objects (ary)
+assert_equal '[true, false]', %q{
+ Ractor.new([[]].freeze) { |ary|
+ [ary.frozen?, ary.first.frozen? ]
+ }.take
+}
+
+# Ractor deep copies frozen objects (str)
+assert_equal '[true, false]', %q{
+ s = String.new.instance_eval { @x = []; freeze}
+ Ractor.new(s) { |s|
+ [s.frozen?, s.instance_variable_get(:@x).frozen?]
+ }.take
+}
+
+# Can not trap with not isolated Proc on non-main ractor
+assert_equal '[:ok, :ok]', %q{
+ a = []
+ Ractor.new{
+ trap(:INT){p :ok}
+ }.take
+ a << :ok
+
+ begin
+ Ractor.new{
+ s = 'str'
+ trap(:INT){p s}
+ }.take
+ rescue => Ractor::RemoteError
+ a << :ok
+ end
+}
+
+# Ractor-local storage
+assert_equal '[nil, "b", "a"]', %q{
+ ans = []
+ Ractor.current[:key] = 'a'
+ r = Ractor.new{
+ Ractor.yield self[:key]
+ self[:key] = 'b'
+ self[:key]
+ }
+ ans << r.take
+ ans << r.take
+ ans << Ractor.current[:key]
+}
+
+###
+### Synchronization tests
+###
+
+N = 100_000
+
+# fstring pool
+assert_equal "#{N}#{N}", %Q{
+ N = #{N}
+ 2.times.map{
+ Ractor.new{
+ N.times{|i| -(i.to_s)}
+ }
+ }.map{|r| r.take}.join
+}
+
+# enc_table
+assert_equal "100", %Q{
+ Ractor.new do
+ loop do
+ Encoding.find("test-enc-#{rand(5_000)}").inspect
+ rescue ArgumentError => e
+ end
+ end
+
+ src = Encoding.find("UTF-8")
+ 100.times{|i|
+ src.replicate("test-enc-\#{i}")
+ }
+}
+
+# Generic ivtbl
+n = N/2
+assert_equal "#{n}#{n}", %Q{
+ 2.times.map{
+ Ractor.new do
+ #{n}.times do
+ obj = ''
+ obj.instance_variable_set("@a", 1)
+ obj.instance_variable_set("@b", 1)
+ obj.instance_variable_set("@c", 1)
+ obj.instance_variable_defined?("@a")
+ end
+ end
+ }.map{|r| r.take}.join
+}
+
+# NameError
+assert_equal "ok", %q{
+ begin
+ bar
+ rescue => err
+ end
+ begin
+ Ractor.new{} << err
+ rescue TypeError
+ 'ok'
+ end
+}
+
+assert_equal "ok", %q{
+ GC.disable
+ Ractor.new {}
+ raise "not ok" unless GC.disable
+
+ foo = []
+ 10.times { foo << 1 }
+
+ GC.start
+
+ 'ok'
+}
+
+# Can yield back values while GC is sweeping [Bug #18117]
+assert_equal "ok", %q{
+ workers = (0...8).map do
+ Ractor.new do
+ loop do
+ 10_000.times.map { Object.new }
+ Ractor.yield Time.now
+ end
+ end
+ end
+
+ 1_000.times { idle_worker, tmp_reporter = Ractor.select(*workers) }
+ "ok"
+}
+
+assert_equal "ok", %q{
+ def foo(*); ->{ super }; end
+ begin
+ Ractor.make_shareable(foo)
+ rescue Ractor::IsolationError
+ "ok"
+ end
+}
+
+assert_equal "ok", %q{
+ def foo(**); ->{ super }; end
+ begin
+ Ractor.make_shareable(foo)
+ rescue Ractor::IsolationError
+ "ok"
+ end
+}
+
+assert_equal "ok", %q{
+ def foo(...); ->{ super }; end
+ begin
+ Ractor.make_shareable(foo)
+ rescue Ractor::IsolationError
+ "ok"
+ end
+}
+
+assert_equal "ok", %q{
+ def foo((x), (y)); ->{ super }; end
+ begin
+ Ractor.make_shareable(foo([], []))
+ rescue Ractor::IsolationError
+ "ok"
+ end
+}
+
+assert_equal "ok", %q{
+ module M
+ def foo
+ @foo
+ end
+ end
+
+ class A
+ include M
+
+ def initialize
+ 100.times { |i| instance_variable_set(:"@var_#{i}", "bad: #{i}") }
+ @foo = 2
+ end
+ end
+
+ class B
+ include M
+
+ def initialize
+ @foo = 1
+ end
+ end
+
+ Ractor.new do
+ b = B.new
+ 100_000.times do
+ raise unless b.foo == 1
+ end
+ end
+
+ a = A.new
+ 100_000.times do
+ raise unless a.foo == 2
+ end
+
+ "ok"
+}
+
+assert_match /\Atest_ractor\.rb:1:\s+warning:\s+Ractor is experimental/, %q{
+ Warning[:experimental] = $VERBOSE = true
+ STDERR.reopen(STDOUT)
+ eval("Ractor.new{}.take", nil, "test_ractor.rb", 1)
+}
+
+end # if !ENV['GITHUB_WORKFLOW']
diff --git a/bootstraptest/test_syntax.rb b/bootstraptest/test_syntax.rb
index a111990a1f..948e2d7809 100644
--- a/bootstraptest/test_syntax.rb
+++ b/bootstraptest/test_syntax.rb
@@ -268,8 +268,10 @@ assert_equal %q{}, %q{
defined?(@@a)
}
assert_equal %q{class variable}, %q{
- @@a = 1
- defined?(@@a)
+ class A
+ @@a = 1
+ defined?(@@a)
+ end
}
assert_equal %q{}, %q{
defined?($a)
@@ -626,7 +628,7 @@ assert_equal '2', %q{
}
assert_match /invalid multibyte char/, %q{
- STDERR.reopen(STDOUT)
+ $stderr = STDOUT
eval("\"\xf0".force_encoding("utf-8"))
}, '[ruby-dev:32429]'
diff --git a/bootstraptest/test_thread.rb b/bootstraptest/test_thread.rb
index d16295de8b..5361828403 100644
--- a/bootstraptest/test_thread.rb
+++ b/bootstraptest/test_thread.rb
@@ -1,4 +1,14 @@
-# Thread and Fiber
+show_limit %q{
+ threads = []
+ begin
+ threads << Thread.new{sleep}
+
+ raise Exception, "skipping" if threads.count >= 10_000
+ rescue Exception => error
+ puts "Thread count: #{threads.count} (#{error})"
+ break
+ end while true
+} if false # disable to pass CI
assert_equal %q{ok}, %q{
Thread.new{
@@ -36,7 +46,7 @@ begin
}
}
rescue ThreadError => e
- :ok if /can't create Thread/ =~ e.message
+ /can't create Thread/ =~ e.message ? :ok : e.message
end
}
assert_equal %q{ok}, %q{
@@ -50,7 +60,7 @@ begin
}
}
rescue ThreadError => e
- :ok if /can't create Thread/ =~ e.message
+ /can't create Thread/ =~ e.message ? :ok : e.message
end
}
assert_equal %q{ok}, %q{
@@ -233,7 +243,7 @@ assert_equal 'true', %{
}
assert_equal 'ok', %{
- open("zzz.rb", "w") do |f|
+ open("zzz_t1.rb", "w") do |f|
f.puts <<-END
begin
Thread.new { fork { GC.start } }.join
@@ -244,7 +254,7 @@ assert_equal 'ok', %{
end
END
end
- require "./zzz.rb"
+ require "./zzz_t1.rb"
$result
}
@@ -299,10 +309,6 @@ assert_equal 'ok', %q{
}, '[ruby-dev:34492]'
assert_normal_exit %q{
- at_exit { Fiber.new{}.resume }
-}
-
-assert_normal_exit %q{
g = enum_for(:local_variables)
loop { g.next }
}, '[ruby-dev:34128]'
@@ -328,10 +334,6 @@ assert_normal_exit %q{
}, '[ruby-dev:34128]'
assert_normal_exit %q{
- Fiber.new(&Object.method(:class_eval)).resume("foo")
-}, '[ruby-dev:34128]'
-
-assert_normal_exit %q{
Thread.new("foo", &Object.method(:class_eval)).join
}, '[ruby-dev:34128]'
@@ -406,7 +408,7 @@ assert_equal 'ok', %q{
}
assert_equal 'ok', %{
- open("zzz.rb", "w") do |f|
+ open("zzz_t2.rb", "w") do |f|
f.puts <<-'end;' # do
begin
m = Thread::Mutex.new
@@ -430,7 +432,7 @@ assert_equal 'ok', %{
end
end;
end
- require "./zzz.rb"
+ require "./zzz_t2.rb"
$result
}
@@ -482,3 +484,17 @@ assert_equal 'foo', %q{
GC.start
f.call.source
}
+assert_normal_exit %q{
+ class C
+ def inspect
+ sleep 0.5
+ 'C!!'
+ end
+ end
+ Thread.new{
+ loop{
+ p C.new
+ }
+ }
+ sleep 0.1
+}, timeout: 5
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
new file mode 100644
index 0000000000..5c655b8f25
--- /dev/null
+++ b/bootstraptest/test_yjit.rb
@@ -0,0 +1,3530 @@
+# Regression test for yielding with autosplat to block with
+# optional parameters. https://github.com/Shopify/yjit/issues/313
+assert_equal '[:a, :b, :a, :b]', %q{
+ def yielder(arg) = yield(arg) + yield(arg)
+
+ yielder([:a, :b]) do |c = :c, d = :d|
+ [c, d]
+ end
+}
+
+# Regression test for GC mishap while doing shape transition
+assert_equal '[:ok]', %q{
+ # [Bug #19601]
+ class RegressionTest
+ def initialize
+ @a = @b = @fourth_ivar_does_shape_transition = nil
+ end
+
+ def extender
+ @first_extended_ivar = [:ok]
+ end
+ end
+
+ GC.stress = true
+
+ # Used to crash due to GC run in rb_ensure_iv_list_size()
+ # not marking the newly allocated [:ok].
+ RegressionTest.new.extender.itself
+}
+
+assert_equal 'true', %q{
+ # regression test for tracking type of locals for too long
+ def local_setting_cmp(five)
+ victim = 5
+ five.define_singleton_method(:respond_to?) do |_, _|
+ victim = nil
+ end
+
+ # +1 makes YJIT track that victim is a number and
+ # defined? calls respond_to? from above indirectly
+ unless (victim + 1) && defined?(five.something)
+ # Would return wrong result if we still think `five` is a number
+ victim.nil?
+ end
+ end
+
+ local_setting_cmp(Object.new)
+ local_setting_cmp(Object.new)
+}
+
+assert_equal '18374962167983112447', %q{
+ # regression test for incorrectly discarding 32 bits of a pointer when it
+ # comes to default values.
+ def large_literal_default(n: 0xff00_fabcafe0_00ff)
+ n
+ end
+
+ def call_graph_root
+ large_literal_default
+ end
+
+ call_graph_root
+ call_graph_root
+}
+
+assert_normal_exit %q{
+ # regression test for a leak caught by an assert on --yjit-call-threshold=2
+ Foo = 1
+
+ eval("def foo = [#{(['Foo,']*256).join}]")
+
+ foo
+ foo
+
+ Object.send(:remove_const, :Foo)
+}
+
+assert_normal_exit %q{
+ # Test to ensure send on overriden c functions
+ # doesn't corrupt the stack
+ class Bar
+ def bar(x)
+ x
+ end
+ end
+
+ class Foo
+ def bar
+ Bar.new
+ end
+ end
+
+ foo = Foo.new
+ # before this change, this line would error
+ # because "s" would still be on the stack
+ # String.to_s is the overridden method here
+ p foo.bar.bar("s".__send__(:to_s))
+}
+
+
+assert_equal '[nil, nil, nil, nil, nil, nil]', %q{
+ [NilClass, TrueClass, FalseClass, Integer, Float, Symbol].each do |klass|
+ klass.class_eval("def foo = @foo")
+ end
+
+ [nil, true, false, 0xFABCAFE, 0.42, :cake].map do |instance|
+ instance.foo
+ instance.foo
+ end
+}
+
+assert_equal '0', %q{
+ # This is a regression test for incomplete invalidation from
+ # opt_setinlinecache. This test might be brittle, so
+ # feel free to remove it in the future if it's too annoying.
+ # This test assumes --yjit-call-threshold=2.
+ module M
+ Foo = 1
+ def foo
+ Foo
+ end
+
+ def pin_self_type_then_foo
+ _ = @foo
+ foo
+ end
+
+ def only_ints
+ 1 + self
+ foo
+ end
+ end
+
+ class Integer
+ include M
+ end
+
+ class Sub
+ include M
+ end
+
+ foo_method = M.instance_method(:foo)
+
+ dbg = ->(message) do
+ return # comment this out to get printouts
+
+ $stderr.puts RubyVM::YJIT.disasm(foo_method)
+ $stderr.puts message
+ end
+
+ 2.times { 42.only_ints }
+
+ dbg["There should be two versions of getinlineache"]
+
+ module M
+ remove_const(:Foo)
+ end
+
+ dbg["There should be no getinlinecaches"]
+
+ 2.times do
+ 42.only_ints
+ rescue NameError => err
+ _ = "caught name error #{err}"
+ end
+
+ dbg["There should be one version of getinlineache"]
+
+ 2.times do
+ Sub.new.pin_self_type_then_foo
+ rescue NameError
+ _ = 'second specialization'
+ end
+
+ dbg["There should be two versions of getinlineache"]
+
+ module M
+ Foo = 1
+ end
+
+ dbg["There should still be two versions of getinlineache"]
+
+ 42.only_ints
+
+ dbg["There should be no getinlinecaches"]
+
+ # Find name of the first VM instruction in M#foo.
+ insns = RubyVM::InstructionSequence.of(foo_method).to_a
+ if defined?(RubyVM::YJIT.blocks_for) && (insns.last.find { Array === _1 }&.first == :opt_getinlinecache)
+ RubyVM::YJIT.blocks_for(RubyVM::InstructionSequence.of(foo_method))
+ .filter { _1.iseq_start_index == 0 }.count
+ else
+ 0 # skip the test
+ end
+}
+
+# Check that frozen objects are respected
+assert_equal 'great', %q{
+ class Foo
+ attr_accessor :bar
+ def initialize
+ @bar = 1
+ freeze
+ end
+ end
+
+ foo = Foo.new
+
+ 5.times do
+ begin
+ foo.bar = 2
+ rescue FrozenError
+ end
+ end
+
+ foo.bar == 1 ? "great" : "NG"
+}
+
+# Check that global variable set works
+assert_equal 'string', %q{
+ def foo
+ $foo = "string"
+ end
+
+ foo
+}
+
+# Check that exceptions work when setting global variables
+assert_equal 'rescued', %q{
+ def set_var
+ $var = 100
+ rescue
+ :rescued
+ end
+
+ set_var
+ trace_var(:$var) { raise }
+ set_var
+}
+
+# Check that global variables work
+assert_equal 'string', %q{
+ $foo = "string"
+
+ def foo
+ $foo
+ end
+
+ foo
+}
+
+# Check that exceptions work when getting global variable
+assert_equal 'rescued', %q{
+ Warning[:deprecated] = true
+
+ module Warning
+ def warn(message)
+ raise
+ end
+ end
+
+ def get_var
+ $=
+ rescue
+ :rescued
+ end
+
+ $VERBOSE = true
+ get_var
+ get_var
+}
+
+# Check that global tracepoints work
+assert_equal 'true', %q{
+ def foo
+ 1
+ end
+
+ foo
+ foo
+ foo
+
+ called = false
+
+ tp = TracePoint.new(:return) { |event|
+ if event.method_id == :foo
+ called = true
+ end
+ }
+ tp.enable
+ foo
+ tp.disable
+ called
+}
+
+# Check that local tracepoints work
+assert_equal 'true', %q{
+ def foo
+ 1
+ end
+
+ foo
+ foo
+ foo
+
+ called = false
+
+ tp = TracePoint.new(:return) { |_| called = true }
+ tp.enable(target: method(:foo))
+ foo
+ tp.disable
+ called
+}
+
+# Make sure that optional param methods return the correct value
+assert_equal '1', %q{
+ def m(ary = [])
+ yield(ary)
+ end
+
+ # Warm the JIT with a 0 param call
+ 2.times { m { } }
+ m(1) { |v| v }
+}
+
+# Test for topn
+assert_equal 'array', %q{
+ def threequals(a)
+ case a
+ when Array
+ "array"
+ when Hash
+ "hash"
+ else
+ "unknown"
+ end
+ end
+
+ threequals([])
+ threequals([])
+ threequals([])
+}
+
+# Test for opt_mod
+assert_equal '2', %q{
+ def mod(a, b)
+ a % b
+ end
+
+ mod(7, 5)
+ mod(7, 5)
+}
+
+# Test for opt_mult
+assert_equal '12', %q{
+ def mult(a, b)
+ a * b
+ end
+
+ mult(6, 2)
+ mult(6, 2)
+}
+
+# Test for opt_div
+assert_equal '3', %q{
+ def div(a, b)
+ a / b
+ end
+
+ div(6, 2)
+ div(6, 2)
+}
+
+# BOP redefined methods work when JIT compiled
+assert_equal 'false', %q{
+ def less_than x
+ x < 10
+ end
+
+ class Integer
+ def < x
+ false
+ end
+ end
+
+ less_than 2
+ less_than 2
+ less_than 2
+}
+
+# BOP redefinition works on Integer#<
+assert_equal 'false', %q{
+ def less_than x
+ x < 10
+ end
+
+ less_than 2
+ less_than 2
+
+ class Integer
+ def < x
+ false
+ end
+ end
+
+ less_than 2
+}
+
+# Putobject, less-than operator, fixnums
+assert_equal '2', %q{
+ def check_index(index)
+ if 0x40000000 < index
+ raise "wat? #{index}"
+ end
+ index
+ end
+ check_index 2
+ check_index 2
+}
+
+# foo leaves a temp on the stack before the call
+assert_equal '6', %q{
+ def bar
+ return 5
+ end
+
+ def foo
+ return 1 + bar
+ end
+
+ foo()
+ retval = foo()
+}
+
+# Method with one arguments
+# foo leaves a temp on the stack before the call
+assert_equal '7', %q{
+ def bar(a)
+ return a + 1
+ end
+
+ def foo
+ return 1 + bar(5)
+ end
+
+ foo()
+ retval = foo()
+}
+
+# Method with two arguments
+# foo leaves a temp on the stack before the call
+assert_equal '0', %q{
+ def bar(a, b)
+ return a - b
+ end
+
+ def foo
+ return 1 + bar(1, 2)
+ end
+
+ foo()
+ retval = foo()
+}
+
+# Passing argument types to callees
+assert_equal '8.5', %q{
+ def foo(x, y)
+ x + y
+ end
+
+ def bar
+ foo(7, 1.5)
+ end
+
+ bar
+ bar
+}
+
+# Recursive Ruby-to-Ruby calls
+assert_equal '21', %q{
+ def fib(n)
+ if n < 2
+ return n
+ end
+
+ return fib(n-1) + fib(n-2)
+ end
+
+ r = fib(8)
+}
+
+# Ruby-to-Ruby call and C call
+assert_normal_exit %q{
+ def bar
+ puts('hi!')
+ end
+
+ def foo
+ bar
+ end
+
+ foo()
+ foo()
+}
+
+# Method aliasing
+assert_equal '42', %q{
+ class Foo
+ def method_a
+ 42
+ end
+
+ alias method_b method_a
+
+ def method_a
+ :somethingelse
+ end
+ end
+
+ @obj = Foo.new
+
+ def test
+ @obj.method_b
+ end
+
+ test
+ test
+}
+
+# Method aliasing with method from parent class
+assert_equal '777', %q{
+ class A
+ def method_a
+ 777
+ end
+ end
+
+ class B < A
+ alias method_b method_a
+ end
+
+ @obj = B.new
+
+ def test
+ @obj.method_b
+ end
+
+ test
+ test
+}
+
+# The hash method is a C function and uses the self argument
+assert_equal 'true', %q{
+ def lehashself
+ hash
+ end
+
+ a = lehashself
+ b = lehashself
+ a == b
+}
+
+# Method redefinition (code invalidation) test
+assert_equal '1', %q{
+ def ret1
+ return 1
+ end
+
+ klass = Class.new do
+ def alias_then_hash(klass, method_to_redefine)
+ # Redefine the method to be ret1
+ klass.alias_method(method_to_redefine, :ret1)
+ hash
+ end
+ end
+
+ instance = klass.new
+
+ i = 0
+ while i < 12
+ if i < 11
+ # Redefine the bar method
+ instance.alias_then_hash(klass, :bar)
+ else
+ # Redefine the hash method to be ret1
+ retval = instance.alias_then_hash(klass, :hash)
+ end
+ i += 1
+ end
+
+ retval
+}
+
+# Code invalidation and opt_getinlinecache
+assert_normal_exit %q{
+ class Foo; end
+
+ # Uses the class constant Foo
+ def use_constant(arg)
+ [Foo.new, arg]
+ end
+
+ def propagate_type
+ i = Array.new
+ i.itself # make it remember that i is on-heap
+ use_constant(i)
+ end
+
+ propagate_type
+ propagate_type
+ use_constant(Foo.new)
+ class Jo; end # bump global constant state
+ use_constant(3)
+}
+
+# Method redefinition (code invalidation) and GC
+assert_equal '7', %q{
+ def bar()
+ return 5
+ end
+
+ def foo()
+ bar()
+ end
+
+ foo()
+ foo()
+
+ def bar()
+ return 7
+ end
+
+ 4.times { GC.start }
+
+ foo()
+ foo()
+}
+
+# Method redefinition with two block versions
+assert_equal '7', %q{
+ def bar()
+ return 5
+ end
+
+ def foo(n)
+ return ((n < 5)? 5:false), bar()
+ end
+
+ foo(4)
+ foo(4)
+ foo(10)
+ foo(10)
+
+ def bar()
+ return 7
+ end
+
+ 4.times { GC.start }
+
+ foo(4)
+ foo(4)[1]
+}
+
+# Method redefinition while the method is on the stack
+assert_equal '[777, 1]', %q{
+ def foo
+ redef()
+ 777
+ end
+
+ def redef
+ # Redefine the global foo
+ eval("def foo; 1; end", TOPLEVEL_BINDING)
+
+ # Collect dead code
+ GC.stress = true
+ GC.start
+
+ # But we will return to the original foo,
+ # which remains alive because it's on the stack
+ end
+
+ # Must produce [777, 1]
+ [foo, foo]
+}
+
+# Test for GC safety. Don't invalidate dead iseqs.
+assert_normal_exit %q{
+ Class.new do
+ def foo
+ itself
+ end
+
+ new.foo
+ new.foo
+ new.foo
+ new.foo
+ end
+
+ 4.times { GC.start }
+ def itself
+ self
+ end
+}
+
+# test setinstancevariable on extended objects
+assert_equal '1', %q{
+ class Extended
+ attr_reader :one
+
+ def write_many
+ @a = 1
+ @b = 2
+ @c = 3
+ @d = 4
+ @one = 1
+ end
+ end
+
+ foo = Extended.new
+ foo.write_many
+ foo.write_many
+ foo.write_many
+}
+
+# test setinstancevariable on embedded objects
+assert_equal '1', %q{
+ class Embedded
+ attr_reader :one
+
+ def write_one
+ @one = 1
+ end
+ end
+
+ foo = Embedded.new
+ foo.write_one
+ foo.write_one
+ foo.write_one
+}
+
+# test setinstancevariable after extension
+assert_equal '[10, 11, 12, 13, 1]', %q{
+ class WillExtend
+ attr_reader :one
+
+ def make_extended
+ @foo1 = 10
+ @foo2 = 11
+ @foo3 = 12
+ @foo4 = 13
+ end
+
+ def write_one
+ @one = 1
+ end
+
+ def read_all
+ [@foo1, @foo2, @foo3, @foo4, @one]
+ end
+ end
+
+ foo = WillExtend.new
+ foo.write_one
+ foo.write_one
+ foo.make_extended
+ foo.write_one
+ foo.read_all
+}
+
+# test setinstancevariable on frozen object
+assert_equal 'object was not modified', %q{
+ class WillFreeze
+ def write
+ @ivar = 1
+ end
+ end
+
+ wf = WillFreeze.new
+ wf.write
+ wf.write
+ wf.freeze
+
+ begin
+ wf.write
+ rescue FrozenError
+ "object was not modified"
+ end
+}
+
+# Test getinstancevariable and inline caches
+assert_equal '6', %q{
+ class Foo
+ def initialize
+ @x1 = 1
+ @x2 = 1
+ @x2 = 1
+ @x3 = 1
+ @x4 = 3
+ end
+
+ def bar
+ x = 1
+ @x4 + @x4
+ end
+ end
+
+ f = Foo.new
+ f.bar
+ f.bar
+}
+
+# Test that getinstancevariable codegen checks for extended table size
+assert_equal "nil\n", %q{
+ class A
+ def read
+ @ins1000
+ end
+ end
+
+ ins = A.new
+ other = A.new
+ 10.times { other.instance_variable_set(:"@otr#{_1}", 'value') }
+ 1001.times { ins.instance_variable_set(:"@ins#{_1}", 'value') }
+
+ ins.read
+ ins.read
+ ins.read
+
+ p other.read
+}
+
+# Test that opt_aref checks the class of the receiver
+assert_equal 'special', %q{
+ def foo(array)
+ array[30]
+ end
+
+ foo([])
+ foo([])
+
+ special = []
+ def special.[](idx)
+ 'special'
+ end
+
+ foo(special)
+}
+
+# Test that object references in generated code get marked and moved
+assert_equal "good", %q{
+ def bar
+ "good"
+ end
+
+ def foo
+ bar
+ end
+
+ foo
+ foo
+
+ begin
+ GC.verify_compaction_references(expand_heap: true, toward: :empty)
+ rescue NotImplementedError
+ # in case compaction isn't supported
+ end
+
+ foo
+}
+
+# Test polymorphic getinstancevariable. T_OBJECT -> T_STRING
+assert_equal 'ok', %q{
+ @hello = @h1 = @h2 = @h3 = @h4 = 'ok'
+ str = ""
+ str.instance_variable_set(:@hello, 'ok')
+
+ public def get
+ @hello
+ end
+
+ get
+ get
+ str.get
+ str.get
+}
+
+# Test polymorphic getinstancevariable, two different classes
+assert_equal 'ok', %q{
+ class Embedded
+ def initialize
+ @ivar = 0
+ end
+
+ def get
+ @ivar
+ end
+ end
+
+ class Extended < Embedded
+ def initialize
+ @v1 = @v2 = @v3 = @v4 = @ivar = 'ok'
+ end
+ end
+
+ embed = Embedded.new
+ extend = Extended.new
+
+ embed.get
+ embed.get
+ extend.get
+ extend.get
+}
+
+# Test megamorphic getinstancevariable
+assert_equal 'ok', %q{
+ parent = Class.new do
+ def initialize
+ @hello = @h1 = @h2 = @h3 = @h4 = 'ok'
+ end
+
+ def get
+ @hello
+ end
+ end
+
+ subclasses = 300.times.map { Class.new(parent) }
+ subclasses.each { _1.new.get }
+ parent.new.get
+}
+
+# Test polymorphic opt_aref. array -> hash
+assert_equal '[42, :key]', %q{
+ def index(obj, idx)
+ obj[idx]
+ end
+
+ index([], 0) # get over compilation threshold
+
+ [
+ index([42], 0),
+ index({0=>:key}, 0),
+ ]
+}
+
+# Test polymorphic opt_aref. hash -> array -> custom class
+assert_equal '[nil, nil, :custom]', %q{
+ def index(obj, idx)
+ obj[idx]
+ end
+
+ custom = Object.new
+ def custom.[](_idx)
+ :custom
+ end
+
+ index({}, 0) # get over compilation threshold
+
+ [
+ index({}, 0),
+ index([], 0),
+ index(custom, 0)
+ ]
+}
+
+# Test polymorphic opt_aref. array -> custom class
+assert_equal '[42, :custom]', %q{
+ def index(obj, idx)
+ obj[idx]
+ end
+
+ custom = Object.new
+ def custom.[](_idx)
+ :custom
+ end
+
+ index([], 0) # get over compilation threshold
+
+ [
+ index([42], 0),
+ index(custom, 0)
+ ]
+}
+
+# Test custom hash method with opt_aref
+assert_equal '[nil, :ok]', %q{
+ def index(obj, idx)
+ obj[idx]
+ end
+
+ custom = Object.new
+ def custom.hash
+ 42
+ end
+
+ h = {custom => :ok}
+
+ [
+ index(h, 0),
+ index(h, custom)
+ ]
+}
+
+# Test default value block for Hash with opt_aref
+assert_equal '[42, :default]', %q{
+ def index(obj, idx)
+ obj[idx]
+ end
+
+ h = Hash.new { :default }
+ h[0] = 42
+
+ [
+ index(h, 0),
+ index(h, 1)
+ ]
+}
+
+# A regression test for making sure cfp->sp is proper when
+# hitting stubs. See :stub-sp-flush:
+assert_equal 'ok', %q{
+ class D
+ def foo
+ Object.new
+ end
+ end
+
+ GC.stress = true
+ 10.times do
+ D.new.foo
+ # ^
+ # This hits a stub with sp_offset > 0
+ end
+
+ :ok
+}
+
+# Test polymorphic callsite, cfunc -> iseq
+assert_equal '[Cfunc, Iseq]', %q{
+ public def call_itself
+ itself # the polymorphic callsite
+ end
+
+ class Cfunc; end
+
+ class Iseq
+ def itself
+ self
+ end
+ end
+
+ call_itself # cross threshold
+
+ [Cfunc.call_itself, Iseq.call_itself]
+}
+
+# Test polymorphic callsite, iseq -> cfunc
+assert_equal '[Iseq, Cfunc]', %q{
+ public def call_itself
+ itself # the polymorphic callsite
+ end
+
+ class Cfunc; end
+
+ class Iseq
+ def itself
+ self
+ end
+ end
+
+ call_itself # cross threshold
+
+ [Iseq.call_itself, Cfunc.call_itself]
+}
+
+# attr_reader method
+assert_equal '[100, 299]', %q{
+ class A
+ attr_reader :foo
+
+ def initialize
+ @foo = 100
+ end
+
+ # Make it extended
+ def fill!
+ @bar = @jojo = @as = @sdfsdf = @foo = 299
+ end
+ end
+
+ def bar(ins)
+ ins.foo
+ end
+
+ ins = A.new
+ oth = A.new
+ oth.fill!
+
+ bar(ins)
+ bar(oth)
+
+ [bar(ins), bar(oth)]
+}
+
+# get ivar on object, then on hash
+assert_equal '[42, 100]', %q{
+ class Hash
+ attr_accessor :foo
+ end
+
+ class A
+ attr_reader :foo
+
+ def initialize
+ @foo = 42
+ end
+ end
+
+ def use(val)
+ val.foo
+ end
+
+
+ h = {}
+ h.foo = 100
+ obj = A.new
+
+ use(obj)
+ [use(obj), use(h)]
+}
+
+# get ivar on String
+assert_equal '[nil, nil, 42, 42]', %q{
+ # @foo to exercise the getinstancevariable instruction
+ public def get_foo
+ @foo
+ end
+
+ get_foo
+ get_foo # compile it for the top level object
+
+ class String
+ attr_reader :foo
+ end
+
+ def run
+ str = String.new
+
+ getter = str.foo
+ insn = str.get_foo
+
+ str.instance_variable_set(:@foo, 42)
+
+ [getter, insn, str.foo, str.get_foo]
+ end
+
+ run
+ run
+}
+
+# splatting an empty array on a getter
+assert_equal '42', %q{
+ @foo = 42
+ module Kernel
+ attr_reader :foo
+ end
+
+ def run
+ foo(*[])
+ end
+
+ run
+ run
+}
+
+# splatting an empty array on a specialized method
+assert_equal 'ok', %q{
+ def run
+ "ok".to_s(*[])
+ end
+
+ run
+ run
+}
+
+# splatting an single element array on a specialized method
+assert_equal '[1]', %q{
+ def run
+ [].<<(*[1])
+ end
+
+ run
+ run
+}
+
+# specialized method with wrong args
+assert_equal 'ok', %q{
+ def run(x)
+ "bad".to_s(123) if x
+ rescue
+ :ok
+ end
+
+ run(false)
+ run(true)
+}
+
+# getinstancevariable on Symbol
+assert_equal '[nil, nil]', %q{
+ # @foo to exercise the getinstancevariable instruction
+ public def get_foo
+ @foo
+ end
+
+ dyn_sym = ("a" + "b").to_sym
+ sym = :static
+
+ # compile get_foo
+ dyn_sym.get_foo
+ dyn_sym.get_foo
+
+ [dyn_sym.get_foo, sym.get_foo]
+}
+
+# attr_reader on Symbol
+assert_equal '[nil, nil]', %q{
+ class Symbol
+ attr_reader :foo
+ end
+
+ public def get_foo
+ foo
+ end
+
+ dyn_sym = ("a" + "b").to_sym
+ sym = :static
+
+ # compile get_foo
+ dyn_sym.get_foo
+ dyn_sym.get_foo
+
+ [dyn_sym.get_foo, sym.get_foo]
+}
+
+# passing too few arguments to method with optional parameters
+assert_equal 'raised', %q{
+ def opt(a, b = 0)
+ end
+
+ def use
+ opt
+ end
+
+ use rescue nil
+ begin
+ use
+ :ng
+ rescue ArgumentError
+ :raised
+ end
+}
+
+# passing too many arguments to method with optional parameters
+assert_equal 'raised', %q{
+ def opt(a, b = 0)
+ end
+
+ def use
+ opt(1, 2, 3, 4)
+ end
+
+ use rescue nil
+ begin
+ use
+ :ng
+ rescue ArgumentError
+ :raised
+ end
+}
+
+# test calling Ruby method with a block
+assert_equal '[1, 2, 42]', %q{
+ def thing(a, b)
+ [a, b, yield]
+ end
+
+ def use
+ thing(1,2) { 42 }
+ end
+
+ use
+ use
+}
+
+# test calling C method with a block
+assert_equal '[42, 42]', %q{
+ def use(array, initial)
+ array.reduce(initial) { |a, b| a + b }
+ end
+
+ use([], 0)
+ [use([2, 2], 38), use([14, 14, 14], 0)]
+}
+
+# test calling block param
+assert_equal '[1, 2, 42]', %q{
+ def foo(&block)
+ block.call
+ end
+
+ [foo {1}, foo {2}, foo {42}]
+}
+
+# test calling without block param
+assert_equal '[1, false, 2, false]', %q{
+ def bar
+ block_given? && yield
+ end
+
+ def foo(&block)
+ bar(&block)
+ end
+
+ [foo { 1 }, foo, foo { 2 }, foo]
+}
+
+# test calling block param failing
+assert_equal '42', %q{
+ def foo(&block)
+ block.call
+ end
+
+ foo {} # warmup
+
+ begin
+ foo
+ rescue NoMethodError => e
+ 42 if nil == e.receiver
+ end
+}
+
+# test calling method taking block param
+assert_equal '[Proc, 1, 2, 3, Proc]', %q{
+ def three(a, b, c, &block)
+ [a, b, c, block.class]
+ end
+
+ def zero(&block)
+ block.class
+ end
+
+ def use_three
+ three(1, 2, 3) {}
+ end
+
+ def use_zero
+ zero {}
+ end
+
+ use_three
+ use_zero
+
+ [use_zero] + use_three
+}
+
+# test building empty array
+assert_equal '[]', %q{
+ def build_arr
+ []
+ end
+
+ build_arr
+ build_arr
+}
+
+# test building array of one element
+assert_equal '[5]', %q{
+ def build_arr(val)
+ [val]
+ end
+
+ build_arr(5)
+ build_arr(5)
+}
+
+# test building array of several element
+assert_equal '[5, 5, 5, 5, 5]', %q{
+ def build_arr(val)
+ [val, val, val, val, val]
+ end
+
+ build_arr(5)
+ build_arr(5)
+}
+
+# test building empty hash
+assert_equal '{}', %q{
+ def build_hash
+ {}
+ end
+
+ build_hash
+ build_hash
+}
+
+# test building hash with values
+assert_equal '{:foo=>:bar}', %q{
+ def build_hash(val)
+ { foo: val }
+ end
+
+ build_hash(:bar)
+ build_hash(:bar)
+}
+
+# test string interpolation with known types
+assert_equal 'foobar', %q{
+ def make_str
+ foo = -"foo"
+ bar = -"bar"
+ "#{foo}#{bar}"
+ end
+
+ make_str
+ make_str
+}
+
+# test string interpolation with unknown types
+assert_equal 'foobar', %q{
+ def make_str(foo, bar)
+ "#{foo}#{bar}"
+ end
+
+ make_str("foo", "bar")
+ make_str("foo", "bar")
+}
+
+# test string interpolation with known non-strings
+assert_equal 'foo123', %q{
+ def make_str
+ foo = -"foo"
+ bar = 123
+ "#{foo}#{bar}"
+ end
+
+ make_str
+ make_str
+}
+
+# test string interpolation with unknown non-strings
+assert_equal 'foo123', %q{
+ def make_str(foo, bar)
+ "#{foo}#{bar}"
+ end
+
+ make_str("foo", 123)
+ make_str("foo", 123)
+}
+
+# test that invalidation of String#to_s doesn't crash
+assert_equal 'meh', %q{
+ def inval_method
+ "".to_s
+ end
+
+ inval_method
+
+ class String
+ def to_s
+ "meh"
+ end
+ end
+
+ inval_method
+}
+
+# test that overriding to_s on a String subclass works consistently
+assert_equal 'meh', %q{
+ class MyString < String
+ def to_s
+ "meh"
+ end
+ end
+
+ def test_to_s(obj)
+ obj.to_s
+ end
+
+ OBJ = MyString.new
+
+ # Should return '' both times
+ test_to_s("")
+ test_to_s("")
+
+ # Can return '' if YJIT optimises String#to_s too aggressively
+ test_to_s(OBJ)
+ test_to_s(OBJ)
+}
+
+# test string interpolation with overridden to_s
+assert_equal 'foo', %q{
+ class String
+ def to_s
+ "bad"
+ end
+ end
+
+ def make_str(foo)
+ "#{foo}"
+ end
+
+ make_str("foo")
+ make_str("foo")
+}
+
+# Test that String unary plus returns the same object ID for an unfrozen string.
+assert_equal 'true', %q{
+ def jittable_method
+ str = "bar"
+
+ old_obj_id = str.object_id
+ uplus_str = +str
+
+ uplus_str.object_id == old_obj_id
+ end
+ jittable_method
+}
+
+# Test that String unary plus returns a different unfrozen string when given a frozen string
+assert_equal 'false', %q{
+ # Logic needs to be inside an ISEQ, such as a method, for YJIT to compile it
+ def jittable_method
+ frozen_str = "foo".freeze
+
+ old_obj_id = frozen_str.object_id
+ uplus_str = +frozen_str
+
+ uplus_str.object_id == old_obj_id || uplus_str.frozen?
+ end
+
+ jittable_method
+}
+
+# String-subclass objects should behave as expected inside string-interpolation via concatstrings
+assert_equal 'monkeys / monkeys, yo!', %q{
+ class MyString < String
+ # This is a terrible idea in production code, but we'd like YJIT to match CRuby
+ def to_s
+ super + ", yo!"
+ end
+ end
+
+ def jittable_method
+ m = MyString.new('monkeys')
+ "#{m} / #{m.to_s}"
+ end
+
+ jittable_method
+}
+
+# String-subclass objects should behave as expected for string equality
+assert_equal 'false', %q{
+ class MyString < String
+ # This is a terrible idea in production code, but we'd like YJIT to match CRuby
+ def ==(b)
+ "#{self}_" == b
+ end
+ end
+
+ def jittable_method
+ ma = MyString.new("a")
+
+ # Check equality with string-subclass receiver
+ ma == "a" || ma != "a_" ||
+ # Check equality with string receiver
+ "a_" == ma || "a" != ma ||
+ # Check equality between string subclasses
+ ma != MyString.new("a_") ||
+ # Make sure "string always equals itself" check isn't used with overridden equality
+ ma == ma
+ end
+ jittable_method
+}
+
+# Test to_s duplicates a string subclass object but not a string
+assert_equal 'false', %q{
+ class MyString < String; end
+
+ def jittable_method
+ a = "a"
+ ma = MyString.new("a")
+
+ a.object_id != a.to_s.object_id ||
+ ma.object_id == ma.to_s.object_id
+ end
+ jittable_method
+}
+
+# Test freeze on string subclass
+assert_equal 'true', %q{
+ class MyString < String; end
+
+ def jittable_method
+ fma = MyString.new("a").freeze
+
+ # Freezing a string subclass should not duplicate it
+ fma.object_id == fma.freeze.object_id
+ end
+ jittable_method
+}
+
+# Test unary minus on string subclass
+assert_equal 'true', %q{
+ class MyString < String; end
+
+ def jittable_method
+ ma = MyString.new("a")
+ fma = MyString.new("a").freeze
+
+ # Unary minus on frozen string subclass should not duplicate it
+ fma.object_id == (-fma).object_id &&
+ # Unary minus on unfrozen string subclass should duplicate it
+ ma.object_id != (-ma).object_id
+ end
+ jittable_method
+}
+
+# Test unary plus on string subclass
+assert_equal 'true', %q{
+ class MyString < String; end
+
+ def jittable_method
+ fma = MyString.new("a").freeze
+
+ # Unary plus on frozen string subclass should not duplicate it
+ fma.object_id != (+fma).object_id
+ end
+ jittable_method
+}
+
+# Test << operator on string subclass
+assert_equal 'abab', %q{
+ class MyString < String; end
+
+ def jittable_method
+ a = -"a"
+ mb = MyString.new("b")
+
+ buf = String.new
+ mbuf = MyString.new
+
+ buf << a << mb
+ mbuf << a << mb
+
+ buf + mbuf
+ end
+ jittable_method
+}
+
+# test invokebuiltin as used in struct assignment
+assert_equal '123', %q{
+ def foo(obj)
+ obj.foo = 123
+ end
+
+ struct = Struct.new(:foo)
+ obj = struct.new
+ foo(obj)
+ foo(obj)
+}
+
+# test invokebuiltin_delegate as used inside Dir.open
+assert_equal '.', %q{
+ def foo(path)
+ Dir.open(path).path
+ end
+
+ foo(".")
+ foo(".")
+}
+
+# test invokebuiltin_delegate_leave in method called from jit
+assert_normal_exit %q{
+ def foo(obj)
+ obj.clone
+ end
+
+ foo(Object.new)
+ foo(Object.new)
+}
+
+# test invokebuiltin_delegate_leave in method called from cfunc
+assert_normal_exit %q{
+ def foo(obj)
+ [obj].map(&:clone)
+ end
+
+ foo(Object.new)
+ foo(Object.new)
+}
+
+# defining TrueClass#!
+assert_equal '[false, false, :ok]', %q{
+ def foo(obj)
+ !obj
+ end
+
+ x = foo(true)
+ y = foo(true)
+
+ class TrueClass
+ def !
+ :ok
+ end
+ end
+
+ z = foo(true)
+
+ [x, y, z]
+}
+
+# defining FalseClass#!
+assert_equal '[true, true, :ok]', %q{
+ def foo(obj)
+ !obj
+ end
+
+ x = foo(false)
+ y = foo(false)
+
+ class FalseClass
+ def !
+ :ok
+ end
+ end
+
+ z = foo(false)
+
+ [x, y, z]
+}
+
+# defining NilClass#!
+assert_equal '[true, true, :ok]', %q{
+ def foo(obj)
+ !obj
+ end
+
+ x = foo(nil)
+ y = foo(nil)
+
+ class NilClass
+ def !
+ :ok
+ end
+ end
+
+ z = foo(nil)
+
+ [x, y, z]
+}
+
+# polymorphic opt_not
+assert_equal '[true, true, false, false, false, false, false]', %q{
+ def foo(obj)
+ !obj
+ end
+
+ foo(0)
+ [foo(nil), foo(false), foo(true), foo([]), foo(0), foo(4.2), foo(:sym)]
+}
+
+# getlocal with 2 levels
+assert_equal '7', %q{
+ def foo(foo, bar)
+ while foo > 0
+ while bar > 0
+ return foo + bar
+ end
+ end
+ end
+
+ foo(5,2)
+ foo(5,2)
+}
+
+# test pattern matching
+assert_equal '[:ok, :ok]', %q{
+ class C
+ def destructure_keys
+ {}
+ end
+ end
+
+ pattern_match = ->(i) do
+ case i
+ in a: 0
+ :ng
+ else
+ :ok
+ end
+ end
+
+ [{}, C.new].map(&pattern_match)
+}
+
+# Call to object with singleton
+assert_equal '123', %q{
+ obj = Object.new
+ def obj.foo
+ 123
+ end
+
+ def foo(obj)
+ obj.foo()
+ end
+
+ foo(obj)
+ foo(obj)
+}
+
+# Call method on an object that has a non-material
+# singleton class.
+# TODO: assert that it takes no side exits? This
+# test case revealed that we were taking exits unnecessarily.
+assert_normal_exit %q{
+ def foo(obj)
+ obj.itself
+ end
+
+ o = Object.new.singleton_class
+ foo(o)
+ foo(o)
+}
+
+# Call to singleton class
+assert_equal '123', %q{
+ class Foo
+ def self.foo
+ 123
+ end
+ end
+
+ def foo(obj)
+ obj.foo()
+ end
+
+ foo(Foo)
+ foo(Foo)
+}
+
+# invokesuper edge case
+assert_equal '[:A, [:A, :B]]', %q{
+ class B
+ def foo = :B
+ end
+
+ class A < B
+ def foo = [:A, super()]
+ end
+
+ A.new.foo
+ A.new.foo # compile A#foo
+
+ class C < A
+ define_method(:bar, A.instance_method(:foo))
+ end
+
+ C.new.bar
+}
+
+# Same invokesuper bytecode, multiple destinations
+assert_equal '[:Forward, :SecondTerminus]', %q{
+ module Terminus
+ def foo = :Terminus
+ end
+
+ module SecondTerminus
+ def foo = :SecondTerminus
+ end
+
+
+ module Forward
+ def foo = [:Forward, super]
+ end
+
+ class B
+ include SecondTerminus
+ end
+
+ class A < B
+ include Terminus
+ include Forward
+ end
+
+ A.new.foo
+ A.new.foo # compile
+
+ class B
+ include Forward
+ alias bar foo
+ end
+
+ # A.ancestors.take(5) == [A, Forward, Terminus, B, Forward, SecondTerminus]
+
+ A.new.bar
+}
+
+# invokesuper calling into itself
+assert_equal '[:B, [:B, :m]]', %q{
+ module M
+ def foo = :m
+ end
+
+ class B
+ include M
+ def foo = [:B, super]
+ end
+
+ ins = B.new
+ ins.singleton_class # materialize the singleton class
+ ins.foo
+ ins.foo # compile
+
+ ins.singleton_class.define_method(:bar, B.instance_method(:foo))
+ ins.bar
+}
+
+# invokesuper changed ancestor
+assert_equal '[:A, [:M, :B]]', %q{
+ class B
+ def foo
+ :B
+ end
+ end
+
+ class A < B
+ def foo
+ [:A, super]
+ end
+ end
+
+ module M
+ def foo
+ [:M, super]
+ end
+ end
+
+ ins = A.new
+ ins.foo
+ ins.foo
+ A.include(M)
+ ins.foo
+}
+
+# invokesuper changed ancestor via prepend
+assert_equal '[:A, [:M, :B]]', %q{
+ class B
+ def foo
+ :B
+ end
+ end
+
+ class A < B
+ def foo
+ [:A, super]
+ end
+ end
+
+ module M
+ def foo
+ [:M, super]
+ end
+ end
+
+ ins = A.new
+ ins.foo
+ ins.foo
+ B.prepend(M)
+ ins.foo
+}
+
+# invokesuper replaced method
+assert_equal '[:A, :Btwo]', %q{
+ class B
+ def foo
+ :B
+ end
+ end
+
+ class A < B
+ def foo
+ [:A, super]
+ end
+ end
+
+ ins = A.new
+ ins.foo
+ ins.foo
+ class B
+ def foo
+ :Btwo
+ end
+ end
+ ins.foo
+}
+
+# Call to fixnum
+assert_equal '[true, false]', %q{
+ def is_odd(obj)
+ obj.odd?
+ end
+
+ is_odd(1)
+ is_odd(1)
+
+ [is_odd(123), is_odd(456)]
+}
+
+# Call to bignum
+assert_equal '[true, false]', %q{
+ def is_odd(obj)
+ obj.odd?
+ end
+
+ bignum = 99999999999999999999
+ is_odd(bignum)
+ is_odd(bignum)
+
+ [is_odd(bignum), is_odd(bignum+1)]
+}
+
+# Call to fixnum and bignum
+assert_equal '[true, false, true, false]', %q{
+ def is_odd(obj)
+ obj.odd?
+ end
+
+ bignum = 99999999999999999999
+ is_odd(bignum)
+ is_odd(bignum)
+ is_odd(123)
+ is_odd(123)
+
+ [is_odd(123), is_odd(456), is_odd(bignum), is_odd(bignum+1)]
+}
+
+# Call to static and dynamic symbol
+assert_equal 'bar', %q{
+ def to_string(obj)
+ obj.to_s
+ end
+
+ to_string(:foo)
+ to_string(:foo)
+ to_string((-"bar").to_sym)
+ to_string((-"bar").to_sym)
+}
+
+# Call to flonum and heap float
+assert_equal '[nil, nil, nil, 1]', %q{
+ def is_inf(obj)
+ obj.infinite?
+ end
+
+ is_inf(0.0)
+ is_inf(0.0)
+ is_inf(1e256)
+ is_inf(1e256)
+
+ [
+ is_inf(0.0),
+ is_inf(1.0),
+ is_inf(1e256),
+ is_inf(1.0/0.0)
+ ]
+}
+
+assert_equal '[1, 2, 3, 4, 5]', %q{
+ def splatarray
+ [*(1..5)]
+ end
+
+ splatarray
+ splatarray
+}
+
+assert_equal '[1, 1, 2, 1, 2, 3]', %q{
+ def expandarray
+ arr = [1, 2, 3]
+
+ a, = arr
+ b, c, = arr
+ d, e, f = arr
+
+ [a, b, c, d, e, f]
+ end
+
+ expandarray
+ expandarray
+}
+
+assert_equal '[1, 1]', %q{
+ def expandarray_useless_splat
+ arr = (1..10).to_a
+
+ a, * = arr
+ b, (*) = arr
+
+ [a, b]
+ end
+
+ expandarray_useless_splat
+ expandarray_useless_splat
+}
+
+assert_equal '[:not_heap, nil, nil]', %q{
+ def expandarray_not_heap
+ a, b, c = :not_heap
+ [a, b, c]
+ end
+
+ expandarray_not_heap
+ expandarray_not_heap
+}
+
+assert_equal '[:not_array, nil, nil]', %q{
+ def expandarray_not_array(obj)
+ a, b, c = obj
+ [a, b, c]
+ end
+
+ obj = Object.new
+ def obj.to_ary
+ [:not_array]
+ end
+
+ expandarray_not_array(obj)
+ expandarray_not_array(obj)
+}
+
+assert_equal '[1, 2, nil]', %q{
+ def expandarray_rhs_too_small
+ a, b, c = [1, 2]
+ [a, b, c]
+ end
+
+ expandarray_rhs_too_small
+ expandarray_rhs_too_small
+}
+
+assert_equal '[1, [2]]', %q{
+ def expandarray_splat
+ a, *b = [1, 2]
+ [a, b]
+ end
+
+ expandarray_splat
+ expandarray_splat
+}
+
+assert_equal '2', %q{
+ def expandarray_postarg
+ *, a = [1, 2]
+ a
+ end
+
+ expandarray_postarg
+ expandarray_postarg
+}
+
+assert_equal '10', %q{
+ obj = Object.new
+ val = nil
+ obj.define_singleton_method(:to_ary) { val = 10; [] }
+
+ def expandarray_always_call_to_ary(object)
+ * = object
+ end
+
+ expandarray_always_call_to_ary(obj)
+ expandarray_always_call_to_ary(obj)
+
+ val
+}
+
+# regression test of local type change
+assert_equal '1.1', %q{
+def bar(baz, quux)
+ if baz.integer?
+ baz, quux = quux, nil
+ end
+ baz.to_s
+end
+
+bar(123, 1.1)
+bar(123, 1.1)
+}
+
+# test enabling a line TracePoint in a C method call
+assert_equal '[[:line, true]]', %q{
+ events = []
+ events.instance_variable_set(
+ :@tp,
+ TracePoint.new(:line) { |tp| events << [tp.event, tp.lineno] if tp.path == __FILE__ }
+ )
+ def events.to_str
+ @tp.enable; ''
+ end
+
+ # Stay in generated code while enabling tracing
+ def events.compiled(obj)
+ String(obj)
+ @tp.disable; __LINE__
+ end
+
+ line = events.compiled(events)
+ events[0][-1] = (events[0][-1] == line)
+
+ events
+}
+
+# test enabling a c_return TracePoint in a C method call
+assert_equal '[[:c_return, :String, :string_alias, "events_to_str"]]', %q{
+ events = []
+ events.instance_variable_set(:@tp, TracePoint.new(:c_return) { |tp| events << [tp.event, tp.method_id, tp.callee_id, tp.return_value] })
+ def events.to_str
+ @tp.enable; 'events_to_str'
+ end
+
+ # Stay in generated code while enabling tracing
+ alias string_alias String
+ def events.compiled(obj)
+ string_alias(obj)
+ @tp.disable
+ end
+
+ events.compiled(events)
+
+ events
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
+
+# test enabling a TracePoint that targets a particular line in a C method call
+assert_equal '[true]', %q{
+ events = []
+ events.instance_variable_set(:@tp, TracePoint.new(:line) { |tp| events << tp.lineno })
+ def events.to_str
+ @tp.enable(target: method(:compiled))
+ ''
+ end
+
+ # Stay in generated code while enabling tracing
+ def events.compiled(obj)
+ String(obj)
+ __LINE__
+ end
+
+ line = events.compiled(events)
+ events[0] = (events[0] == line)
+
+ events
+}
+
+# test enabling tracing in the middle of splatarray
+assert_equal '[true]', %q{
+ events = []
+ obj = Object.new
+ obj.instance_variable_set(:@tp, TracePoint.new(:line) { |tp| events << tp.lineno })
+ def obj.to_a
+ @tp.enable(target: method(:compiled))
+ []
+ end
+
+ # Enable tracing in the middle of the splatarray instruction
+ def obj.compiled(obj)
+ * = *obj
+ __LINE__
+ end
+
+ obj.compiled([])
+ line = obj.compiled(obj)
+ events[0] = (events[0] == line)
+
+ events
+}
+
+# test enabling tracing in the middle of opt_aref. Different since the codegen
+# for it ends in a jump.
+assert_equal '[true]', %q{
+ def lookup(hash, tp)
+ hash[42]
+ tp.disable; __LINE__
+ end
+
+ lines = []
+ tp = TracePoint.new(:line) { lines << _1.lineno if _1.path == __FILE__ }
+
+ lookup(:foo, tp)
+ lookup({}, tp)
+
+ enable_tracing_on_missing = Hash.new { tp.enable }
+
+ expected_line = lookup(enable_tracing_on_missing, tp)
+
+ lines[0] = true if lines[0] == expected_line
+
+ lines
+}
+
+# test enabling c_call tracing before compiling
+assert_equal '[[:c_call, :itself]]', %q{
+ def shouldnt_compile
+ itself
+ end
+
+ events = []
+ tp = TracePoint.new(:c_call) { |tp| events << [tp.event, tp.method_id] }
+
+ # assume first call compiles
+ tp.enable { shouldnt_compile }
+
+ events
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
+
+# test enabling c_return tracing before compiling
+assert_equal '[[:c_return, :itself, main]]', %q{
+ def shouldnt_compile
+ itself
+ end
+
+ events = []
+ tp = TracePoint.new(:c_return) { |tp| events << [tp.event, tp.method_id, tp.return_value] }
+
+ # assume first call compiles
+ tp.enable { shouldnt_compile }
+
+ events
+} unless defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # MJIT calls extra Ruby methods
+
+# test c_call invalidation
+assert_equal '[[:c_call, :itself]]', %q{
+ # enable the event once to make sure invalidation
+ # happens the second time we enable it
+ TracePoint.new(:c_call) {}.enable{}
+
+ def compiled
+ itself
+ end
+
+ # assume first call compiles
+ compiled
+
+ events = []
+ tp = TracePoint.new(:c_call) { |tp| events << [tp.event, tp.method_id] }
+ tp.enable { compiled }
+
+ events
+}
+
+# test enabling tracing for a suspended fiber
+assert_equal '[[:return, 42]]', %q{
+ def traced_method
+ Fiber.yield
+ 42
+ end
+
+ events = []
+ tp = TracePoint.new(:return) { events << [_1.event, _1.return_value] }
+ # assume first call compiles
+ fiber = Fiber.new { traced_method }
+ fiber.resume
+ tp.enable(target: method(:traced_method))
+ fiber.resume
+
+ events
+}
+
+# test compiling on non-tracing ractor then running on a tracing one
+assert_equal '[:itself]', %q{
+ def traced_method
+ itself
+ end
+
+ tracing_ractor = Ractor.new do
+ # 1: start tracing
+ events = []
+ tp = TracePoint.new(:c_call) { events << _1.method_id }
+ tp.enable
+ Ractor.yield(nil)
+
+ # 3: run compiled method on tracing ractor
+ Ractor.yield(nil)
+ traced_method
+
+ events
+ ensure
+ tp&.disable
+ end
+
+ tracing_ractor.take
+
+ # 2: compile on non tracing ractor
+ traced_method
+
+ tracing_ractor.take
+ tracing_ractor.take
+}
+
+# Try to hit a lazy branch stub while another ractor enables tracing
+assert_equal '42', %q{
+ def compiled(arg)
+ if arg
+ arg + 1
+ else
+ itself
+ itself
+ end
+ end
+
+ ractor = Ractor.new do
+ compiled(false)
+ Ractor.yield(nil)
+ compiled(41)
+ end
+
+ tp = TracePoint.new(:line) { itself }
+ ractor.take
+ tp.enable
+
+ ractor.take
+}
+
+# Test equality with changing types
+assert_equal '[true, false, false, false]', %q{
+ def eq(a, b)
+ a == b
+ end
+
+ [
+ eq("foo", "foo"),
+ eq("foo", "bar"),
+ eq(:foo, "bar"),
+ eq("foo", :bar)
+ ]
+}
+
+# Redefined String eq
+assert_equal 'true', %q{
+ class String
+ def ==(other)
+ true
+ end
+ end
+
+ def eq(a, b)
+ a == b
+ end
+
+ eq("foo", "bar")
+ eq("foo", "bar")
+}
+
+# Redefined Integer eq
+assert_equal 'true', %q{
+ class Integer
+ def ==(other)
+ true
+ end
+ end
+
+ def eq(a, b)
+ a == b
+ end
+
+ eq(1, 2)
+ eq(1, 2)
+}
+
+# aset on array with invalid key
+assert_normal_exit %q{
+ def foo(arr)
+ arr[:foo] = 123
+ end
+
+ foo([1]) rescue nil
+ foo([1]) rescue nil
+}
+
+# test ractor exception on when setting ivar
+assert_equal '42', %q{
+ class A
+ def self.foo
+ _foo = 1
+ _bar = 2
+ begin
+ @bar = _foo + _bar
+ rescue Ractor::IsolationError
+ 42
+ end
+ end
+ end
+
+ A.foo
+ A.foo
+
+ Ractor.new { A.foo }.take
+}
+
+assert_equal '["plain", "special", "sub", "plain"]', %q{
+ def foo(arg)
+ arg.to_s
+ end
+
+ class Sub < String
+ end
+
+ special = String.new("special")
+ special.singleton_class
+
+ [
+ foo("plain"),
+ foo(special),
+ foo(Sub.new("sub")),
+ foo("plain")
+ ]
+}
+
+assert_equal '["sub", "sub"]', %q{
+ def foo(arg)
+ arg.to_s
+ end
+
+ class Sub < String
+ def to_s
+ super
+ end
+ end
+
+ sub = Sub.new("sub")
+
+ [foo(sub), foo(sub)]
+}
+
+assert_equal '[1]', %q{
+ def kwargs(value:)
+ value
+ end
+
+ 5.times.map { kwargs(value: 1) }.uniq
+}
+
+assert_equal '[:ok]', %q{
+ def kwargs(value:)
+ value
+ end
+
+ 5.times.map { kwargs() rescue :ok }.uniq
+}
+
+assert_equal '[:ok]', %q{
+ def kwargs(a:, b: nil)
+ value
+ end
+
+ 5.times.map { kwargs(b: 123) rescue :ok }.uniq
+}
+
+assert_equal '[[1, 2]]', %q{
+ def kwargs(left:, right:)
+ [left, right]
+ end
+
+ 5.times.flat_map do
+ [
+ kwargs(left: 1, right: 2),
+ kwargs(right: 2, left: 1)
+ ]
+ end.uniq
+}
+
+assert_equal '[[1, 2]]', %q{
+ def kwargs(lead, kwarg:)
+ [lead, kwarg]
+ end
+
+ 5.times.map { kwargs(1, kwarg: 2) }.uniq
+}
+
+# optional and keyword args
+assert_equal '[[1, 2, 3]]', %q{
+ def opt_and_kwargs(a, b=2, c: nil)
+ [a,b,c]
+ end
+
+ 5.times.map { opt_and_kwargs(1, c: 3) }.uniq
+}
+
+assert_equal '[[1, 2, 3]]', %q{
+ def opt_and_kwargs(a, b=nil, c: nil)
+ [a,b,c]
+ end
+
+ 5.times.map { opt_and_kwargs(1, 2, c: 3) }.uniq
+}
+
+# Bug #18453
+assert_equal '[[1, nil, 2]]', %q{
+ def opt_and_kwargs(a = {}, b: nil, c: nil)
+ [a, b, c]
+ end
+
+ 5.times.map { opt_and_kwargs(1, c: 2) }.uniq
+}
+
+assert_equal '[[{}, nil, 1]]', %q{
+ def opt_and_kwargs(a = {}, b: nil, c: nil)
+ [a, b, c]
+ end
+
+ 5.times.map { opt_and_kwargs(c: 1) }.uniq
+}
+
+# leading and keyword arguments are swapped into the right order
+assert_equal '[[1, 2, 3, 4, 5, 6]]', %q{
+ def kwargs(five, six, a:, b:, c:, d:)
+ [a, b, c, d, five, six]
+ end
+
+ 5.times.flat_map do
+ [
+ kwargs(5, 6, a: 1, b: 2, c: 3, d: 4),
+ kwargs(5, 6, a: 1, b: 2, d: 4, c: 3),
+ kwargs(5, 6, a: 1, c: 3, b: 2, d: 4),
+ kwargs(5, 6, a: 1, c: 3, d: 4, b: 2),
+ kwargs(5, 6, a: 1, d: 4, b: 2, c: 3),
+ kwargs(5, 6, a: 1, d: 4, c: 3, b: 2),
+ kwargs(5, 6, b: 2, a: 1, c: 3, d: 4),
+ kwargs(5, 6, b: 2, a: 1, d: 4, c: 3),
+ kwargs(5, 6, b: 2, c: 3, a: 1, d: 4),
+ kwargs(5, 6, b: 2, c: 3, d: 4, a: 1),
+ kwargs(5, 6, b: 2, d: 4, a: 1, c: 3),
+ kwargs(5, 6, b: 2, d: 4, c: 3, a: 1),
+ kwargs(5, 6, c: 3, a: 1, b: 2, d: 4),
+ kwargs(5, 6, c: 3, a: 1, d: 4, b: 2),
+ kwargs(5, 6, c: 3, b: 2, a: 1, d: 4),
+ kwargs(5, 6, c: 3, b: 2, d: 4, a: 1),
+ kwargs(5, 6, c: 3, d: 4, a: 1, b: 2),
+ kwargs(5, 6, c: 3, d: 4, b: 2, a: 1),
+ kwargs(5, 6, d: 4, a: 1, b: 2, c: 3),
+ kwargs(5, 6, d: 4, a: 1, c: 3, b: 2),
+ kwargs(5, 6, d: 4, b: 2, a: 1, c: 3),
+ kwargs(5, 6, d: 4, b: 2, c: 3, a: 1),
+ kwargs(5, 6, d: 4, c: 3, a: 1, b: 2),
+ kwargs(5, 6, d: 4, c: 3, b: 2, a: 1)
+ ]
+ end.uniq
+}
+
+# implicit hashes get skipped and don't break compilation
+assert_equal '[[:key]]', %q{
+ def implicit(hash)
+ hash.keys
+ end
+
+ 5.times.map { implicit(key: :value) }.uniq
+}
+
+# default values on keywords don't mess up argument order
+assert_equal '[2]', %q{
+ def default_value
+ 1
+ end
+
+ def default_expression(value: default_value)
+ value
+ end
+
+ 5.times.map { default_expression(value: 2) }.uniq
+}
+
+# constant default values on keywords
+assert_equal '[3]', %q{
+ def default_expression(value: 3)
+ value
+ end
+
+ 5.times.map { default_expression }.uniq
+}
+
+# non-constant default values on keywords
+assert_equal '[3]', %q{
+ def default_value
+ 3
+ end
+
+ def default_expression(value: default_value)
+ value
+ end
+
+ 5.times.map { default_expression }.uniq
+}
+
+# reordered optional kwargs
+assert_equal '[[100, 1]]', %q{
+ def foo(capacity: 100, max: nil)
+ [capacity, max]
+ end
+
+ 5.times.map { foo(max: 1) }.uniq
+}
+
+# invalid lead param
+assert_equal 'ok', %q{
+ def bar(baz: 2)
+ baz
+ end
+
+ def foo
+ bar(1, baz: 123)
+ end
+
+ begin
+ foo
+ foo
+ rescue ArgumentError => e
+ print "ok"
+ end
+}
+
+# reordered required kwargs
+assert_equal '[[1, 2, 3, 4]]', %q{
+ def foo(default1: 1, required1:, default2: 3, required2:)
+ [default1, required1, default2, required2]
+ end
+
+ 5.times.map { foo(required1: 2, required2: 4) }.uniq
+}
+
+# reordered default expression kwargs
+assert_equal '[[:one, :two, 3]]', %q{
+ def foo(arg1: (1+0), arg2: (2+0), arg3: (3+0))
+ [arg1, arg2, arg3]
+ end
+
+ 5.times.map { foo(arg2: :two, arg1: :one) }.uniq
+}
+
+# complex kwargs
+assert_equal '[[1, 2, 3, 4]]', %q{
+ def foo(required:, specified: 999, simple_default: 3, complex_default: "4".to_i)
+ [required, specified, simple_default, complex_default]
+ end
+
+ 5.times.map { foo(specified: 2, required: 1) }.uniq
+}
+
+# cfunc kwargs
+assert_equal '{:foo=>123}', %q{
+ def foo(bar)
+ bar.store(:value, foo: 123)
+ bar[:value]
+ end
+
+ foo({})
+ foo({})
+}
+
+# cfunc kwargs
+assert_equal '{:foo=>123}', %q{
+ def foo(bar)
+ bar.replace(foo: 123)
+ end
+
+ foo({})
+ foo({})
+}
+
+# cfunc kwargs
+assert_equal '{:foo=>123, :bar=>456}', %q{
+ def foo(bar)
+ bar.replace(foo: 123, bar: 456)
+ end
+
+ foo({})
+ foo({})
+}
+
+# variadic cfunc kwargs
+assert_equal '{:foo=>123}', %q{
+ def foo(bar)
+ bar.merge(foo: 123)
+ end
+
+ foo({})
+ foo({})
+}
+
+# optimized cfunc kwargs
+assert_equal 'false', %q{
+ def foo
+ :foo.eql?(foo: :foo)
+ end
+
+ foo
+ foo
+}
+
+# attr_reader on frozen object
+assert_equal 'false', %q{
+ class Foo
+ attr_reader :exception
+
+ def failed?
+ !exception.nil?
+ end
+ end
+
+ foo = Foo.new.freeze
+ foo.failed?
+ foo.failed?
+}
+
+# regression test for doing kwarg shuffle before checking for interrupts
+assert_equal 'ok', %q{
+ def new_media_drop(attributes:, product_drop:, context:, sources:)
+ nil.nomethod rescue nil # force YJIT to bail to side exit
+
+ [attributes, product_drop, context, sources]
+ end
+
+ def load_medias(product_drop: nil, raw_medias:, context:)
+ raw_medias.map do |raw_media|
+ case new_media_drop(context: context, attributes: raw_media, product_drop: product_drop, sources: [])
+ in [Hash, ProductDrop, Context, Array]
+ else
+ raise "bad shuffle"
+ end
+ end
+ end
+
+ class Context; end
+
+ class ProductDrop
+ attr_reader :title
+ def initialize(title)
+ @title = title
+ end
+ end
+
+ # Make a thread so we have thread switching interrupts
+ th = Thread.new do
+ while true; end
+ end
+ 1_000.times do |i|
+ load_medias(product_drop: ProductDrop.new("foo"), raw_medias: [{}, {}], context: Context.new)
+ end
+ th.kill.join
+
+ :ok
+}
+
+# regression test for tracing attr_accessor methods.
+assert_equal "true", %q{
+ c = Class.new do
+ attr_accessor :x
+ alias y x
+ alias y= x=
+ end
+ obj = c.new
+
+ ar_meth = obj.method(:x)
+ aw_meth = obj.method(:x=)
+ aar_meth = obj.method(:y)
+ aaw_meth = obj.method(:y=)
+ events = []
+ trace = TracePoint.new(:c_call, :c_return){|tp|
+ next if tp.path != __FILE__
+ next if tp.method_id == :call
+ case tp.event
+ when :c_call
+ events << [tp.event, tp.method_id, tp.callee_id]
+ when :c_return
+ events << [tp.event, tp.method_id, tp.callee_id, tp.return_value]
+ end
+ }
+ test_proc = proc do
+ obj.x = 1
+ obj.x
+ obj.y = 2
+ obj.y
+ aw_meth.call(1)
+ ar_meth.call
+ aaw_meth.call(2)
+ aar_meth.call
+ end
+ test_proc.call # populate call caches
+ trace.enable(&test_proc)
+ expected = [
+ [:c_call, :x=, :x=],
+ [:c_return, :x=, :x=, 1],
+ [:c_call, :x, :x],
+ [:c_return, :x, :x, 1],
+ [:c_call, :x=, :y=],
+ [:c_return, :x=, :y=, 2],
+ [:c_call, :x, :y],
+ [:c_return, :x, :y, 2],
+ ] * 2
+
+ expected == events
+}
+
+# duphash
+assert_equal '{:foo=>123}', %q{
+ def foo
+ {foo: 123}
+ end
+
+ foo
+ foo
+}
+
+# newhash
+assert_equal '{:foo=>2}', %q{
+ def foo
+ {foo: 1+1}
+ end
+
+ foo
+ foo
+}
+
+# block invalidation edge case
+assert_equal 'undef', %q{
+ class A
+ def foo(arg)
+ arg.times { A.remove_method(:bar) }
+ self
+ end
+
+ def bar
+ 4
+ end
+
+ def use(arg)
+ # two consecutive sends. When bar is removed, the return address
+ # for calling it is already on foo's control frame
+ foo(arg).bar
+ rescue NoMethodError
+ :undef
+ end
+ end
+
+ A.new.use 0
+ A.new.use 0
+ A.new.use 1
+}
+
+# block invalidation edge case
+assert_equal 'ok', %q{
+ class A
+ Good = :ng
+ def foo(arg)
+ arg.times { A.const_set(:Good, :ok) }
+ self
+ end
+
+ def id(arg)
+ arg
+ end
+
+ def use(arg)
+ # send followed by an opt_getinlinecache.
+ # The return address remains on the control frame
+ # when opt_getinlinecache is invalidated.
+ foo(arg).id(Good)
+ end
+ end
+
+ A.new.use 0
+ A.new.use 0
+ A.new.use 1
+}
+
+assert_equal 'ok', %q{
+ # test hitting a branch stub when out of memory
+ def nimai(jita)
+ if jita
+ :ng
+ else
+ :ok
+ end
+ end
+
+ nimai(true)
+ nimai(true)
+
+ RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
+
+ nimai(false)
+}
+
+assert_equal 'new', %q{
+ # test block invalidation while out of memory
+ def foo
+ :old
+ end
+
+ def test
+ foo
+ end
+
+ def bar
+ :bar
+ end
+
+
+ test
+ test
+
+ RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
+
+ # Old simulat_omm! leaves one byte of space and this fills it up
+ bar
+ bar
+
+ def foo
+ :new
+ end
+
+ test
+}
+
+assert_equal 'ok', %q{
+ # Try to compile new method while OOM
+ def foo
+ :ok
+ end
+
+ RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
+
+ foo
+ foo
+}
+
+# struct aref embedded
+assert_equal '2', %q{
+ def foo(s)
+ s.foo
+ end
+
+ S = Struct.new(:foo)
+ foo(S.new(1))
+ foo(S.new(2))
+}
+
+# struct aref non-embedded
+assert_equal '4', %q{
+ def foo(s)
+ s.d
+ end
+
+ S = Struct.new(:a, :b, :c, :d, :e)
+ foo(S.new(1,2,3,4,5))
+ foo(S.new(1,2,3,4,5))
+}
+
+# struct aset embedded
+assert_equal '123', %q{
+ def foo(s)
+ s.foo = 123
+ end
+
+ s = Struct.new(:foo).new
+ foo(s)
+ s = Struct.new(:foo).new
+ foo(s)
+ s.foo
+}
+
+# struct aset non-embedded
+assert_equal '[1, 2, 3, 4, 5]', %q{
+ def foo(s)
+ s.a = 1
+ s.b = 2
+ s.c = 3
+ s.d = 4
+ s.e = 5
+ end
+
+ S = Struct.new(:a, :b, :c, :d, :e)
+ s = S.new
+ foo(s)
+ s = S.new
+ foo(s)
+ [s.a, s.b, s.c, s.d, s.e]
+}
+
+# struct aref too many args
+assert_equal 'ok', %q{
+ def foo(s)
+ s.foo(:bad)
+ end
+
+ s = Struct.new(:foo).new
+ foo(s) rescue :ok
+ foo(s) rescue :ok
+}
+
+# struct aset too many args
+assert_equal 'ok', %q{
+ def foo(s)
+ s.set_foo(123, :bad)
+ end
+
+ s = Struct.new(:foo) do
+ alias :set_foo :foo=
+ end
+ foo(s) rescue :ok
+ foo(s) rescue :ok
+}
+
+# File.join is a cfunc accepting variable arguments as a Ruby array (argc = -2)
+assert_equal 'foo/bar', %q{
+ def foo
+ File.join("foo", "bar")
+ end
+
+ foo
+ foo
+}
+
+# File.join is a cfunc accepting variable arguments as a Ruby array (argc = -2)
+assert_equal '', %q{
+ def foo
+ File.join()
+ end
+
+ foo
+ foo
+}
+
+# Make sure we're correctly reading RStruct's as.ary union for embedded RStructs
+assert_equal '3,12', %q{
+ pt_struct = Struct.new(:x, :y)
+ p = pt_struct.new(3, 12)
+ def pt_inspect(pt)
+ "#{pt.x},#{pt.y}"
+ end
+
+ # Make sure pt_inspect is JITted
+ 10.times { pt_inspect(p) }
+
+ # Make sure it's returning '3,12' instead of e.g. '3,false'
+ pt_inspect(p)
+}
+
+# Regression test for deadlock between branch_stub_hit and ractor_receive_if
+assert_equal '10', %q{
+ r = Ractor.new Ractor.current do |main|
+ main << 1
+ main << 2
+ main << 3
+ main << 4
+ main << 5
+ main << 6
+ main << 7
+ main << 8
+ main << 9
+ main << 10
+ end
+
+ a = []
+ a << Ractor.receive_if{|msg| msg == 10}
+ a << Ractor.receive_if{|msg| msg == 9}
+ a << Ractor.receive_if{|msg| msg == 8}
+ a << Ractor.receive_if{|msg| msg == 7}
+ a << Ractor.receive_if{|msg| msg == 6}
+ a << Ractor.receive_if{|msg| msg == 5}
+ a << Ractor.receive_if{|msg| msg == 4}
+ a << Ractor.receive_if{|msg| msg == 3}
+ a << Ractor.receive_if{|msg| msg == 2}
+ a << Ractor.receive_if{|msg| msg == 1}
+
+ a.length
+}
+
+# checktype
+assert_equal 'false', %q{
+ def function()
+ [1, 2] in [Integer, String]
+ end
+ function()
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_ATTRSET)
+assert_equal 'foo', %q{
+ class Foo
+ attr_writer :foo
+
+ def foo()
+ self.foo = "foo"
+ end
+ end
+ foo = Foo.new
+ foo.foo
+}
+
+# anytostring, intern
+assert_equal 'true', %q{
+ def foo()
+ :"#{true}"
+ end
+ foo()
+}
+
+# toregexp, objtostring
+assert_equal '/true/', %q{
+ def foo()
+ /#{true}/
+ end
+ foo().inspect
+}
+
+# concatstrings, objtostring
+assert_equal '9001', %q{
+ def foo()
+ "#{9001}"
+ end
+ foo()
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_CFUNC)
+assert_equal 'nil', %q{
+ def foo
+ nil.inspect # argc: 0
+ end
+ foo
+}
+assert_equal '4', %q{
+ def foo
+ 2.pow(2) # argc: 1
+ end
+ foo
+}
+assert_equal 'aba', %q{
+ def foo
+ "abc".tr("c", "a") # argc: 2
+ end
+ foo
+}
+assert_equal 'true', %q{
+ def foo
+ respond_to?(:inspect) # argc: -1
+ end
+ foo
+}
+assert_equal '["a", "b"]', %q{
+ def foo
+ "a\nb".lines(chomp: true) # kwargs
+ end
+ foo
+}
+
+# invokebuiltin
+assert_equal '123', %q{
+ def foo(obj)
+ obj.foo = 123
+ end
+
+ struct = Struct.new(:foo)
+ obj = struct.new
+ foo(obj)
+}
+
+# invokebuiltin_delegate
+assert_equal '.', %q{
+ def foo(path)
+ Dir.open(path).path
+ end
+ foo(".")
+}
+
+# opt_invokebuiltin_delegate_leave
+assert_equal '[0]', %q{"\x00".unpack("c")}
+
+# opt_send_without_block (VM_METHOD_TYPE_ISEQ)
+assert_equal '1', %q{
+ def foo = 1
+ def bar = foo
+ bar
+}
+assert_equal '[1, 2, 3]', %q{
+ def foo(a, b) = [1, a, b]
+ def bar = foo(2, 3)
+ bar
+}
+assert_equal '[1, 2, 3, 4, 5, 6]', %q{
+ def foo(a, b, c:, d:, e: 0, f: 6) = [a, b, c, d, e, f]
+ def bar = foo(1, 2, c: 3, d: 4, e: 5)
+ bar
+}
+assert_equal '[1, 2, 3, 4]', %q{
+ def foo(a, b = 2) = [a, b]
+ def bar = foo(1) + foo(3, 4)
+ bar
+}
+
+assert_equal '1', %q{
+ def foo(a) = a
+ def bar = foo(1) { 2 }
+ bar
+}
+assert_equal '[1, 2]', %q{
+ def foo(a, &block) = [a, block.call]
+ def bar = foo(1) { 2 }
+ bar
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_IVAR)
+assert_equal 'foo', %q{
+ class Foo
+ attr_reader :foo
+
+ def initialize
+ @foo = "foo"
+ end
+ end
+ Foo.new.foo
+}
+
+# opt_send_without_block (VM_METHOD_TYPE_OPTIMIZED)
+assert_equal 'foo', %q{
+ Foo = Struct.new(:bar)
+ Foo.new("bar").bar = "foo"
+}
+assert_equal 'foo', %q{
+ Foo = Struct.new(:bar)
+ Foo.new("foo").bar
+}
+
+# getblockparamproxy
+assert_equal 'foo', %q{
+ def foo(&block)
+ block.call
+ end
+ foo { "foo" }
+}
+
+# getblockparam
+assert_equal 'foo', %q{
+ def foo(&block)
+ block
+ end
+ foo { "foo" }.call
+}
+
+assert_equal '[1, 2]', %q{
+ def foo
+ x = [2]
+ [1, *x]
+ end
+
+ foo
+ foo
+}
+
+# respond_to? with changing symbol
+assert_equal 'false', %q{
+ def foo(name)
+ :sym.respond_to?(name)
+ end
+ foo(:to_s)
+ foo(:to_s)
+ foo(:not_exist)
+}
+
+# respond_to? with method being defined
+assert_equal 'true', %q{
+ def foo
+ :sym.respond_to?(:not_yet_defined)
+ end
+ foo
+ foo
+ module Kernel
+ def not_yet_defined = true
+ end
+ foo
+}
+
+# respond_to? with undef method
+assert_equal 'false', %q{
+ module Kernel
+ def to_be_removed = true
+ end
+ def foo
+ :sym.respond_to?(:to_be_removed)
+ end
+ foo
+ foo
+ class Object
+ undef_method :to_be_removed
+ end
+ foo
+}
+
+# respond_to? with respond_to_missing?
+assert_equal 'true', %q{
+ class Foo
+ end
+ def foo(x)
+ x.respond_to?(:bar)
+ end
+ foo(Foo.new)
+ foo(Foo.new)
+ class Foo
+ def respond_to_missing?(*) = true
+ end
+ foo(Foo.new)
+}
+
+# bmethod
+assert_equal '[1, 2, 3]', %q{
+ one = 1
+ define_method(:foo) do
+ one
+ end
+
+ 3.times.map { |i| foo + i }
+}
+
+# return inside bmethod
+assert_equal 'ok', %q{
+ define_method(:foo) do
+ 1.tap { return :ok }
+ end
+
+ foo
+}
+
+# bmethod optional and keywords
+assert_equal '[[1, nil, 2]]', %q{
+ define_method(:opt_and_kwargs) do |a = {}, b: nil, c: nil|
+ [a, b, c]
+ end
+
+ 5.times.map { opt_and_kwargs(1, c: 2) }.uniq
+}
+
+# bmethod with forwarded block
+assert_equal '2', %q{
+ define_method(:foo) do |&block|
+ block.call
+ end
+
+ def bar(&block)
+ foo(&block)
+ end
+
+ bar { 1 }
+ bar { 2 }
+}
+
+# bmethod with forwarded block and arguments
+assert_equal '5', %q{
+ define_method(:foo) do |n, &block|
+ n + block.call
+ end
+
+ def bar(n, &block)
+ foo(n, &block)
+ end
+
+ bar(0) { 1 }
+ bar(3) { 2 }
+}
+
+# bmethod with forwarded unwanted block
+assert_equal '1', %q{
+ one = 1
+ define_method(:foo) do
+ one
+ end
+
+ def bar(&block)
+ foo(&block)
+ end
+
+ bar { }
+ bar { }
+}
+
+# test for return stub lifetime issue
+assert_equal '1', %q{
+ def foo(n)
+ if n == 2
+ return 1.times { Object.define_method(:foo) {} }
+ end
+
+ foo(n + 1)
+ end
+
+ foo(1)
+}
+
+# case-when with redefined ===
+assert_equal 'ok', %q{
+ class Symbol
+ def ===(a)
+ true
+ end
+ end
+
+ def cw(arg)
+ case arg
+ when :b
+ :ok
+ when 4
+ :ng
+ end
+ end
+
+ cw(4)
+}
+
+assert_normal_exit %{
+ class Bug20997
+ def foo(&) = self.class.name(&)
+
+ new.foo
+ end
+}
diff --git a/bootstraptest/test_yjit_30k_ifelse.rb b/bootstraptest/test_yjit_30k_ifelse.rb
new file mode 100644