diff options
71 files changed, 1666 insertions, 1138 deletions
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 4675239528..03c0f7a4f5 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -25,13 +25,6 @@ jobs: strategy: matrix: include: - - os: 2019 - vc: 2015 - vcvars: '10.0.14393.0 -vcvars_ver=14.0' # The oldest Windows 10 SDK w/ VC++ 2015 toolset (v140) - test_task: check - - os: 2019 - vc: 2019 - test_task: check - os: 2022 vc: 2019 vcvars: '10.0.22621.0 -vcvars_ver=14.2' # The defautl Windows 11 SDK and toolset are broken at windows-2022 @@ -65,7 +58,6 @@ jobs: env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} - OS_VER: windows-${{ matrix.os < 2022 && '2019' || matrix.os }} VCPKG_DEFAULT_TRIPLET: ${{ matrix.target || 'x64' }}-windows RUBY_OPT_DIR: ${{ matrix.os == '11-arm' && 'C' || 'D' }}:/a/ruby/ruby/src/vcpkg_installed/%VCPKG_DEFAULT_TRIPLET% @@ -66,9 +66,9 @@ The following default gems are updated. * json 2.11.3 * optparse 0.7.0.dev.2 * prism 1.4.0 -* psych 5.2.3 +* psych 5.2.4 * stringio 3.1.8.dev -* strscan 3.1.4.dev +* strscan 3.1.5.dev * uri 1.0.3 The following bundled gems are added. diff --git a/addr2line.c b/addr2line.c index dc0892e308..745364cc0f 100644 --- a/addr2line.c +++ b/addr2line.c @@ -2175,9 +2175,8 @@ fill_lines(int num_traces, void **traces, int check_debuglink, } } - if (offset == -1) { + if (offset == 0) { /* main executable */ - offset = 0; if (dynsym_shdr && dynstr_shdr) { char *strtab = file + dynstr_shdr->sh_offset; ElfW(Sym) *symtab = (ElfW(Sym) *)(file + dynsym_shdr->sh_offset); @@ -189,6 +189,7 @@ COMMONOBJS = array.$(OBJEXT) \ $(YJIT_LIBOBJ) \ $(ZJIT_OBJ) \ $(ZJIT_LIBOBJ) \ + $(JIT_OBJ) \ $(COROUTINE_OBJ) \ $(DTRACE_OBJ) \ $(BUILTIN_ENCOBJS) \ @@ -9156,6 +9157,241 @@ iseq.$(OBJEXT): {$(VPATH)}vm_debug.h iseq.$(OBJEXT): {$(VPATH)}vm_opts.h iseq.$(OBJEXT): {$(VPATH)}vm_sync.h iseq.$(OBJEXT): {$(VPATH)}yjit.h +jit.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h +jit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h +jit.$(OBJEXT): $(CCAN_DIR)/list/list.h +jit.$(OBJEXT): $(CCAN_DIR)/str/str.h +jit.$(OBJEXT): $(hdrdir)/ruby/ruby.h +jit.$(OBJEXT): $(top_srcdir)/internal/array.h +jit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h +jit.$(OBJEXT): $(top_srcdir)/internal/class.h +jit.$(OBJEXT): $(top_srcdir)/internal/compilers.h +jit.$(OBJEXT): $(top_srcdir)/internal/gc.h +jit.$(OBJEXT): $(top_srcdir)/internal/imemo.h +jit.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h +jit.$(OBJEXT): $(top_srcdir)/internal/serial.h +jit.$(OBJEXT): $(top_srcdir)/internal/set_table.h +jit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h +jit.$(OBJEXT): $(top_srcdir)/internal/variable.h +jit.$(OBJEXT): $(top_srcdir)/internal/vm.h +jit.$(OBJEXT): $(top_srcdir)/internal/warnings.h +jit.$(OBJEXT): $(top_srcdir)/prism/defines.h +jit.$(OBJEXT): $(top_srcdir)/prism/encoding.h +jit.$(OBJEXT): $(top_srcdir)/prism/node.h +jit.$(OBJEXT): $(top_srcdir)/prism/options.h +jit.$(OBJEXT): $(top_srcdir)/prism/pack.h +jit.$(OBJEXT): $(top_srcdir)/prism/parser.h +jit.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h +jit.$(OBJEXT): $(top_srcdir)/prism/prism.h +jit.$(OBJEXT): $(top_srcdir)/prism/regexp.h +jit.$(OBJEXT): $(top_srcdir)/prism/static_literals.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h +jit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h +jit.$(OBJEXT): {$(VPATH)}assert.h +jit.$(OBJEXT): {$(VPATH)}atomic.h +jit.$(OBJEXT): {$(VPATH)}backward/2/assume.h +jit.$(OBJEXT): {$(VPATH)}backward/2/attributes.h +jit.$(OBJEXT): {$(VPATH)}backward/2/bool.h +jit.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h +jit.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h +jit.$(OBJEXT): {$(VPATH)}backward/2/limits.h +jit.$(OBJEXT): {$(VPATH)}backward/2/long_long.h +jit.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h +jit.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h +jit.$(OBJEXT): {$(VPATH)}builtin.h +jit.$(OBJEXT): {$(VPATH)}config.h +jit.$(OBJEXT): {$(VPATH)}constant.h +jit.$(OBJEXT): {$(VPATH)}debug_counter.h +jit.$(OBJEXT): {$(VPATH)}defines.h +jit.$(OBJEXT): {$(VPATH)}encoding.h +jit.$(OBJEXT): {$(VPATH)}id.h +jit.$(OBJEXT): {$(VPATH)}id_table.h +jit.$(OBJEXT): {$(VPATH)}insns.def +jit.$(OBJEXT): {$(VPATH)}insns.inc +jit.$(OBJEXT): {$(VPATH)}insns_info.inc +jit.$(OBJEXT): {$(VPATH)}intern.h +jit.$(OBJEXT): {$(VPATH)}internal.h +jit.$(OBJEXT): {$(VPATH)}internal/abi.h +jit.$(OBJEXT): {$(VPATH)}internal/anyargs.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h +jit.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h +jit.$(OBJEXT): {$(VPATH)}internal/assume.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/cold.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/const.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/error.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/format.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/pure.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/warning.h +jit.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h +jit.$(OBJEXT): {$(VPATH)}internal/cast.h +jit.$(OBJEXT): {$(VPATH)}internal/compiler_is.h +jit.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h +jit.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h +jit.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h +jit.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h +jit.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h +jit.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h +jit.$(OBJEXT): {$(VPATH)}internal/compiler_since.h +jit.$(OBJEXT): {$(VPATH)}internal/config.h +jit.$(OBJEXT): {$(VPATH)}internal/constant_p.h +jit.$(OBJEXT): {$(VPATH)}internal/core.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rarray.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rclass.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rdata.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rfile.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rhash.h +jit.$(OBJEXT): {$(VPATH)}internal/core/robject.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rstring.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h +jit.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h +jit.$(OBJEXT): {$(VPATH)}internal/ctype.h +jit.$(OBJEXT): {$(VPATH)}internal/dllexport.h +jit.$(OBJEXT): {$(VPATH)}internal/dosish.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/re.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/string.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h +jit.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h +jit.$(OBJEXT): {$(VPATH)}internal/error.h +jit.$(OBJEXT): {$(VPATH)}internal/eval.h +jit.$(OBJEXT): {$(VPATH)}internal/event.h +jit.$(OBJEXT): {$(VPATH)}internal/fl_type.h +jit.$(OBJEXT): {$(VPATH)}internal/gc.h +jit.$(OBJEXT): {$(VPATH)}internal/glob.h +jit.$(OBJEXT): {$(VPATH)}internal/globals.h +jit.$(OBJEXT): {$(VPATH)}internal/has/attribute.h +jit.$(OBJEXT): {$(VPATH)}internal/has/builtin.h +jit.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h +jit.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h +jit.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h +jit.$(OBJEXT): {$(VPATH)}internal/has/extension.h +jit.$(OBJEXT): {$(VPATH)}internal/has/feature.h +jit.$(OBJEXT): {$(VPATH)}internal/has/warning.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/array.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/class.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/compar.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/complex.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/cont.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/dir.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/enum.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/error.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/eval.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/file.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/hash.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/io.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/load.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/object.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/parse.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/proc.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/process.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/random.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/range.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/rational.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/re.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/select.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/signal.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/string.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/struct.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/thread.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/time.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/variable.h +jit.$(OBJEXT): {$(VPATH)}internal/intern/vm.h +jit.$(OBJEXT): {$(VPATH)}internal/interpreter.h +jit.$(OBJEXT): {$(VPATH)}internal/iterator.h +jit.$(OBJEXT): {$(VPATH)}internal/memory.h +jit.$(OBJEXT): {$(VPATH)}internal/method.h +jit.$(OBJEXT): {$(VPATH)}internal/module.h +jit.$(OBJEXT): {$(VPATH)}internal/newobj.h +jit.$(OBJEXT): {$(VPATH)}internal/scan_args.h +jit.$(OBJEXT): {$(VPATH)}internal/special_consts.h +jit.$(OBJEXT): {$(VPATH)}internal/static_assert.h +jit.$(OBJEXT): {$(VPATH)}internal/stdalign.h +jit.$(OBJEXT): {$(VPATH)}internal/stdbool.h +jit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h +jit.$(OBJEXT): {$(VPATH)}internal/symbol.h +jit.$(OBJEXT): {$(VPATH)}internal/value.h +jit.$(OBJEXT): {$(VPATH)}internal/value_type.h +jit.$(OBJEXT): {$(VPATH)}internal/variable.h +jit.$(OBJEXT): {$(VPATH)}internal/warning_push.h +jit.$(OBJEXT): {$(VPATH)}internal/xmalloc.h +jit.$(OBJEXT): {$(VPATH)}iseq.h +jit.$(OBJEXT): {$(VPATH)}jit.c +jit.$(OBJEXT): {$(VPATH)}method.h +jit.$(OBJEXT): {$(VPATH)}missing.h +jit.$(OBJEXT): {$(VPATH)}node.h +jit.$(OBJEXT): {$(VPATH)}onigmo.h +jit.$(OBJEXT): {$(VPATH)}oniguruma.h +jit.$(OBJEXT): {$(VPATH)}prism/ast.h +jit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h +jit.$(OBJEXT): {$(VPATH)}prism/version.h +jit.$(OBJEXT): {$(VPATH)}prism_compile.h +jit.$(OBJEXT): {$(VPATH)}ruby_assert.h +jit.$(OBJEXT): {$(VPATH)}ruby_atomic.h +jit.$(OBJEXT): {$(VPATH)}rubyparser.h +jit.$(OBJEXT): {$(VPATH)}shape.h +jit.$(OBJEXT): {$(VPATH)}st.h +jit.$(OBJEXT): {$(VPATH)}subst.h +jit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h +jit.$(OBJEXT): {$(VPATH)}thread_native.h +jit.$(OBJEXT): {$(VPATH)}vm_callinfo.h +jit.$(OBJEXT): {$(VPATH)}vm_core.h +jit.$(OBJEXT): {$(VPATH)}vm_debug.h +jit.$(OBJEXT): {$(VPATH)}vm_opts.h +jit.$(OBJEXT): {$(VPATH)}vm_sync.h load.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h load.$(OBJEXT): $(CCAN_DIR)/list/list.h @@ -9992,6 +10228,7 @@ marshal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +marshal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h marshal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h @@ -10613,6 +10850,7 @@ miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +miniinit.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h miniinit.$(OBJEXT): {$(VPATH)}internal/attr/pure.h @@ -16937,6 +17175,7 @@ signal.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h signal.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +signal.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h signal.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h signal.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h signal.$(OBJEXT): {$(VPATH)}internal/attr/pure.h @@ -17705,6 +17944,7 @@ string.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h string.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h string.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h string.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +string.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h string.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h string.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h string.$(OBJEXT): {$(VPATH)}internal/attr/pure.h @@ -18170,6 +18410,7 @@ symbol.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h +symbol.$(OBJEXT): {$(VPATH)}internal/attr/nonstring.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h symbol.$(OBJEXT): {$(VPATH)}internal/attr/pure.h diff --git a/configure.ac b/configure.ac index c76db94d84..4bcd614f44 100644 --- a/configure.ac +++ b/configure.ac @@ -3967,6 +3967,7 @@ AS_CASE(["${YJIT_SUPPORT}"], LDFLAGS="$LDFLAGS -lpthread -lc++abi" ]) YJIT_OBJ='yjit.$(OBJEXT)' + JIT_OBJ='jit.$(OBJEXT)' AS_IF([test x"$YJIT_SUPPORT" != "xyes" ], [ AC_DEFINE_UNQUOTED(YJIT_SUPPORT, [$YJIT_SUPPORT]) ]) @@ -4008,6 +4009,7 @@ AS_CASE(["${ZJIT_SUPPORT}"], LDFLAGS="$LDFLAGS -lpthread -lc++abi" ]) ZJIT_OBJ='zjit.$(OBJEXT)' + JIT_OBJ='jit.$(OBJEXT)' AS_IF([test x"$ZJIT_SUPPORT" != "xyes" ], [ AC_DEFINE_UNQUOTED(ZJIT_SUPPORT, [$ZJIT_SUPPORT]) ]) @@ -4025,8 +4027,9 @@ AC_SUBST(ZJIT_CARGO_BUILD_ARGS)dnl for selecting Rust build profiles AC_SUBST(YJIT_LIBS)dnl for optionally building the Rust parts of YJIT AC_SUBST(YJIT_OBJ)dnl for optionally building the C parts of YJIT AC_SUBST(ZJIT_SUPPORT)dnl what flavor of ZJIT the Ruby build includes -AC_SUBST(ZJIT_LIBS)dnl for optionally building the Rust parts of YJIT -AC_SUBST(ZJIT_OBJ)dnl for optionally building the C parts of YJIT +AC_SUBST(ZJIT_LIBS)dnl for optionally building the Rust parts of ZJIT +AC_SUBST(ZJIT_OBJ)dnl for optionally building the C parts of ZJIT +AC_SUBST(JIT_OBJ)dnl for optionally building C glue code for Rust FFI } [begin]_group "build section" && { diff --git a/doc/string/new.rdoc b/doc/string/new.rdoc index 1d44291f76..e2752d6e1f 100644 --- a/doc/string/new.rdoc +++ b/doc/string/new.rdoc @@ -1,34 +1,38 @@ -Returns a new \String that is a copy of +string+. +Returns a new \String object containing the given +string+. -With no arguments, returns the empty string with the Encoding <tt>ASCII-8BIT</tt>: +The +options+ are optional keyword options (see below). - s = String.new - s # => "" - s.encoding # => #<Encoding:ASCII-8BIT> +With no argument given and keyword +encoding+ also not given, +returns an empty string with the Encoding <tt>ASCII-8BIT</tt>: -With optional argument +string+ and no keyword arguments, -returns a copy of +string+ with the same encoding: + s = String.new # => "" + s.encoding # => #<Encoding:ASCII-8BIT> - String.new('foo') # => "foo" - String.new('тест') # => "тест" - String.new('こんにちは') # => "こんにちは" +With argument +string+ given and keyword option +encoding+ not given, +returns a new string with the same encoding as +string+: + + s0 = 'foo'.encode(Encoding::UTF_16) + s1 = String.new(s0) + s1.encoding # => #<Encoding:UTF-16 (dummy)> (Unlike \String.new, a {string literal}[rdoc-ref:syntax/literals.rdoc@String+Literals] like <tt>''</tt> or a {here document literal}[rdoc-ref:syntax/literals.rdoc@Here+Document+Literals] always has {script encoding}[rdoc-ref:encodings.rdoc@Script+Encoding].) -With optional keyword argument +encoding+, returns a copy of +string+ -with the specified encoding; +With keyword option +encoding+ given, +returns a string with the specified encoding; the +encoding+ may be an Encoding object, an encoding name, or an encoding name alias: + String.new(encoding: Encoding::US_ASCII).encoding # => #<Encoding:US-ASCII> + String.new('', encoding: Encoding::US_ASCII).encoding # => #<Encoding:US-ASCII> String.new('foo', encoding: Encoding::US_ASCII).encoding # => #<Encoding:US-ASCII> String.new('foo', encoding: 'US-ASCII').encoding # => #<Encoding:US-ASCII> String.new('foo', encoding: 'ASCII').encoding # => #<Encoding:US-ASCII> The given encoding need not be valid for the string's content, -and that validity is not checked: +and its validity is not checked: s = String.new('こんにちは', encoding: 'ascii') s.valid_encoding? # => false @@ -37,19 +41,11 @@ But the given +encoding+ itself is checked: String.new('foo', encoding: 'bar') # Raises ArgumentError. -With optional keyword argument +capacity+, returns a copy of +string+ -(or an empty string, if +string+ is not given); -the given +capacity+ is advisory only, +With keyword option +capacity+ given, +the given value is advisory only, and may or may not set the size of the internal buffer, which may in turn affect performance: - String.new(capacity: 1) - String.new('foo', capacity: 4096) - -Note that Ruby strings are null-terminated internally, so the internal -buffer size will be one or more bytes larger than the requested capacity -depending on the encoding. - -The +string+, +encoding+, and +capacity+ arguments may all be used together: - - String.new('hello', encoding: 'UTF-8', capacity: 25) + String.new('foo', capacity: 1) # Buffer size is at least 4 (includes terminal null byte). + String.new('foo', capacity: 4096) # Buffer size is at least 4; + # may be equal to, greater than, or less than 4096. diff --git a/enc/depend b/enc/depend index 2918a90a05..dcf65a129b 100644 --- a/enc/depend +++ b/enc/depend @@ -7016,6 +7016,7 @@ enc/trans/iso2022.$(OBJEXT): internal/attr/nodiscard.h enc/trans/iso2022.$(OBJEXT): internal/attr/noexcept.h enc/trans/iso2022.$(OBJEXT): internal/attr/noinline.h enc/trans/iso2022.$(OBJEXT): internal/attr/nonnull.h +enc/trans/iso2022.$(OBJEXT): internal/attr/nonstring.h enc/trans/iso2022.$(OBJEXT): internal/attr/noreturn.h enc/trans/iso2022.$(OBJEXT): internal/attr/packed_struct.h enc/trans/iso2022.$(OBJEXT): internal/attr/pure.h diff --git a/enc/trans/iso2022.trans b/enc/trans/iso2022.trans index a25a4a12a1..b0c635d574 100644 --- a/enc/trans/iso2022.trans +++ b/enc/trans/iso2022.trans @@ -1,4 +1,5 @@ #include "transcode_data.h" +#include "ruby/internal/attr/nonstring.h" <% map = { @@ -436,7 +437,7 @@ rb_cp50221_encoder = { /* JIS0201 to JIS0208 conversion table */ enum {tbl0208_num = 0xDF - 0xA1 + 1}; -static const char tbl0208[tbl0208_num][2] = { +RBIMPL_ATTR_NONSTRING() static const char tbl0208[tbl0208_num][2] = { "\x21\x23", "\x21\x56", "\x21\x57", "\x21\x22", "\x21\x26", "\x25\x72", "\x25\x21", "\x25\x23", "\x25\x25", "\x25\x27", "\x25\x29", "\x25\x63", diff --git a/ext/digest/defs.h b/ext/digest/defs.h index 77a134f364..9b11f4eca9 100644 --- a/ext/digest/defs.h +++ b/ext/digest/defs.h @@ -16,4 +16,26 @@ # define __END_DECLS #endif +#define RB_DIGEST_DIAGNOSTIC(compiler, op, flag) _Pragma(STRINGIZE(compiler diagnostic op flag)) +#ifdef RBIMPL_WARNING_IGNORED +# define RB_DIGEST_WARNING_IGNORED(flag) RBIMPL_WARNING_IGNORED(flag) +# define RB_DIGEST_WARNING_PUSH() RBIMPL_WARNING_PUSH() +# define RB_DIGEST_WARNING_POP() RBIMPL_WARNING_POP() +#elif defined(__clang__) +# define RB_DIGEST_WARNING_IGNORED(flag) RB_DIGEST_DIAGNOSTIC(clang, ignored, #flag) +# define RB_DIGEST_WARNING_PUSH() _Pragma("clang diagnostic push") +# define RB_DIGEST_WARNING_POP() _Pragma("clang diagnostic pop") +#else /* __GNUC__ */ +# define RB_DIGEST_WARNING_IGNORED(flag) RB_DIGEST_DIAGNOSTIC(GCC, ignored, #flag) +# define RB_DIGEST_WARNING_PUSH() _Pragma("GCC diagnostic push") +# define RB_DIGEST_WARNING_POP() _Pragma("GCC diagnostic pop") +#endif +#ifdef RBIMPL_HAS_WARNING +# define RB_DIGEST_HAS_WARNING(_) RBIMPL_HAS_WARNING(_) +#elif defined(__has_warning) +# define RB_DIGEST_HAS_WARNING(_) __has_warning(_) +#else +# define RB_DIGEST_HAS_WARNING(_) 0 +#endif + #endif /* DEFS_H */ diff --git a/ext/digest/digest_conf.rb b/ext/digest/digest_conf.rb index 36a7d75289..099d20fcbe 100644 --- a/ext/digest/digest_conf.rb +++ b/ext/digest/digest_conf.rb @@ -2,14 +2,16 @@ def digest_conf(name) unless with_config("bundled-#{name}") - cc = with_config("common-digest") - if cc != false or /\b#{name}\b/ =~ cc - if File.exist?("#$srcdir/#{name}cc.h") and - have_header("CommonCrypto/CommonDigest.h") - $defs << "-D#{name.upcase}_USE_COMMONDIGEST" - $headers << "#{name}cc.h" - return :commondigest - end + case cc = with_config("common-digest", true) + when true, false + else + cc = cc.split(/[\s,]++/).any? {|pat| File.fnmatch?(pat, name)} + end + if cc and File.exist?("#$srcdir/#{name}cc.h") and + have_header("CommonCrypto/CommonDigest.h") + $defs << "-D#{name.upcase}_USE_COMMONDIGEST" + $headers << "#{name}cc.h" + return :commondigest end end $objs << "#{name}.#{$OBJEXT}" diff --git a/ext/digest/md5/md5cc.h b/ext/digest/md5/md5cc.h index 657f573f85..a002c17604 100644 --- a/ext/digest/md5/md5cc.h +++ b/ext/digest/md5/md5cc.h @@ -2,14 +2,6 @@ #include <CommonCrypto/CommonDigest.h> #ifdef __GNUC__ -# define RB_DIGEST_DIAGNOSTIC(compiler, op, flag) _Pragma(STRINGIZE(compiler diagnostic op flag)) -# ifdef RBIMPL_WARNING_IGNORED -# define RB_DIGEST_WARNING_IGNORED(flag) RBIMPL_WARNING_IGNORED(flag) -# elif defined(__clang__) -# define RB_DIGEST_WARNING_IGNORED(flag) RB_DIGEST_DIAGNOSTIC(clang, ignored, #flag) -# else /* __GNUC__ */ -# define RB_DIGEST_WARNING_IGNORED(flag) RB_DIGEST_DIAGNOSTIC(GCC, ignored, #flag) -# endif RB_DIGEST_WARNING_IGNORED(-Wdeprecated-declarations) /* Suppress deprecation warnings of MD5 from Xcode 11.1 */ /* Although we know MD5 is deprecated too, provide just for backward diff --git a/ext/digest/md5/md5init.c b/ext/digest/md5/md5init.c index b81fd94864..c919060587 100644 --- a/ext/digest/md5/md5init.c +++ b/ext/digest/md5/md5init.c @@ -3,6 +3,7 @@ #include <ruby/ruby.h> #include "../digest.h" +#include "../defs.h" #if defined(MD5_USE_COMMONDIGEST) #include "md5cc.h" #else diff --git a/ext/digest/sha1/sha1.c b/ext/digest/sha1/sha1.c index 5311227549..244fed7a3e 100644 --- a/ext/digest/sha1/sha1.c +++ b/ext/digest/sha1/sha1.c @@ -232,8 +232,14 @@ void SHA1_Update(SHA1_CTX *context, const uint8_t *data, size_t len) if ((j + len) > 63) { (void)memcpy(&context->buffer[j], data, (i = 64-j)); SHA1_Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) + for ( ; i + 63 < len; i += 64) { + RB_DIGEST_WARNING_PUSH(); +#if defined(__GNUC__) && !defined(__clang__) + RB_DIGEST_WARNING_IGNORED(-Wstringop-overread); +#endif SHA1_Transform(context->state, &data[i]); + RB_DIGEST_WARNING_POP(); + } j = 0; } else { i = 0; diff --git a/ext/json/generator/extconf.rb b/ext/json/generator/extconf.rb index e44890e2ed..60372ee558 100644 --- a/ext/json/generator/extconf.rb +++ b/ext/json/generator/extconf.rb @@ -18,7 +18,7 @@ else return 0; } SRC - $defs.push("-DENABLE_SIMD") + $defs.push("-DJSON_ENABLE_SIMD") end end @@ -29,7 +29,7 @@ else return 0; } SRC - $defs.push("-DENABLE_SIMD") + $defs.push("-DJSON_ENABLE_SIMD") end have_header('cpuid.h') diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 536c2aa1b7..06ab8010d9 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -112,7 +112,7 @@ typedef struct _search_state { const char *cursor; FBuffer *buffer; -#ifdef ENABLE_SIMD +#ifdef HAVE_SIMD const char *chunk_base; const char *chunk_end; bool has_matches; @@ -124,7 +124,7 @@ typedef struct _search_state { #else #error "Unknown SIMD Implementation." #endif /* HAVE_SIMD_NEON */ -#endif /* ENABLE_SIMD */ +#endif /* HAVE_SIMD */ } search_state; #if (defined(__GNUC__ ) || defined(__clang__)) @@ -189,15 +189,11 @@ static inline FORCE_INLINE void escape_UTF8_char_basic(search_state *search) case '\r': fbuffer_append(search->buffer, "\\r", 2); break; case '\t': fbuffer_append(search->buffer, "\\t", 2); break; default: { - if (ch < ' ') { - const char *hexdig = "0123456789abcdef"; - char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; - scratch[4] = hexdig[(ch >> 4) & 0xf]; - scratch[5] = hexdig[ch & 0xf]; - fbuffer_append(search->buffer, scratch, 6); - } else { - fbuffer_append_char(search->buffer, ch); - } + const char *hexdig = "0123456789abcdef"; + char scratch[6] = { '\\', 'u', '0', '0', 0, 0 }; + scratch[4] = hexdig[(ch >> 4) & 0xf]; + scratch[5] = hexdig[ch & 0xf]; + fbuffer_append(search->buffer, scratch, 6); break; } } @@ -265,7 +261,7 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) search->cursor = (search->ptr += ch_len); } -#ifdef ENABLE_SIMD +#ifdef HAVE_SIMD static inline FORCE_INLINE char *copy_remaining_bytes(search_state *search, unsigned long vec_len, unsigned long len) { @@ -537,7 +533,7 @@ static inline TARGET_SSE2 FORCE_INLINE unsigned char search_escape_basic_sse2(se #endif /* HAVE_SIMD_SSE2 */ -#endif /* ENABLE_SIMD */ +#endif /* HAVE_SIMD */ static const unsigned char script_safe_escape_table[256] = { // ASCII Control Characters @@ -1302,11 +1298,11 @@ static void generate_json_string(FBuffer *buffer, struct generate_json_data *dat search.cursor = search.ptr; search.end = search.ptr + len; -#ifdef ENABLE_SIMD +#ifdef HAVE_SIMD search.matches_mask = 0; search.has_matches = false; search.chunk_base = NULL; -#endif /* ENABLE_SIMD */ +#endif /* HAVE_SIMD */ switch(rb_enc_str_coderange(obj)) { case ENC_CODERANGE_7BIT: @@ -2174,7 +2170,7 @@ void Init_generator(void) switch(find_simd_implementation()) { -#ifdef ENABLE_SIMD +#ifdef HAVE_SIMD #ifdef HAVE_SIMD_NEON case SIMD_NEON: search_escape_basic_impl = search_escape_basic_neon; @@ -2185,7 +2181,7 @@ void Init_generator(void) search_escape_basic_impl = search_escape_basic_sse2; break; #endif /* HAVE_SIMD_SSE2 */ -#endif /* ENABLE_SIMD */ +#endif /* HAVE_SIMD */ default: search_escape_basic_impl = search_escape_basic; break; diff --git a/ext/json/generator/simd.h b/ext/json/generator/simd.h index 2fbc93169d..b12890cb09 100644 --- a/ext/json/generator/simd.h +++ b/ext/json/generator/simd.h @@ -4,7 +4,7 @@ typedef enum { SIMD_SSE2 } SIMD_Implementation; -#ifdef ENABLE_SIMD +#ifdef JSON_ENABLE_SIMD #ifdef __clang__ #if __has_builtin(__builtin_ctzll) @@ -56,6 +56,7 @@ static SIMD_Implementation find_simd_implementation(void) { return SIMD_NEON; } +#define HAVE_SIMD 1 #define HAVE_SIMD_NEON 1 uint8x16x4_t load_uint8x16_4(const unsigned char *table) { @@ -74,6 +75,7 @@ uint8x16x4_t load_uint8x16_4(const unsigned char *table) { #ifdef HAVE_X86INTRIN_H #include <x86intrin.h> +#define HAVE_SIMD 1 #define HAVE_SIMD_SSE2 1 #ifdef HAVE_CPUID_H @@ -101,7 +103,7 @@ static SIMD_Implementation find_simd_implementation(void) { #endif /* HAVE_X86INTRIN_H */ #endif /* X86_64 Support */ -#endif /* ENABLE_SIMD */ +#endif /* JSON_ENABLE_SIMD */ #ifndef FIND_SIMD_IMPLEMENTATION_DEFINED static SIMD_Implementation find_simd_implementation(void) { diff --git a/ext/psych/lib/psych/class_loader.rb b/ext/psych/lib/psych/class_loader.rb index 50efc35ee2..c8f509720a 100644 --- a/ext/psych/lib/psych/class_loader.rb +++ b/ext/psych/lib/psych/class_loader.rb @@ -6,6 +6,7 @@ module Psych class ClassLoader # :nodoc: BIG_DECIMAL = 'BigDecimal' COMPLEX = 'Complex' + DATA = 'Data' unless RUBY_VERSION < "3.2" DATE = 'Date' DATE_TIME = 'DateTime' EXCEPTION = 'Exception' diff --git a/ext/psych/lib/psych/core_ext.rb b/ext/psych/lib/psych/core_ext.rb index 0721a133c3..950b20f2d6 100644 --- a/ext/psych/lib/psych/core_ext.rb +++ b/ext/psych/lib/psych/core_ext.rb @@ -17,3 +17,17 @@ end if defined?(::IRB) require_relative 'y' end + + +# TODO: how best to check for builtin Set? +if defined?(::Set) && Object.const_source_location(:Set) == ["ruby", 0] + class Set + def encode_with(coder) + coder["hash"] = to_h + end + + def init_with(coder) + replace(coder["hash"].keys) + end + end +end diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb index d91563c861..0adcdae5f4 100644 --- a/ext/psych/lib/psych/versions.rb +++ b/ext/psych/lib/psych/versions.rb @@ -2,7 +2,7 @@ module Psych # The version of Psych you are using - VERSION = '5.2.3' + VERSION = '5.2.4' if RUBY_ENGINE == 'jruby' DEFAULT_SNAKEYAML_VERSION = '2.9'.freeze diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb index f0b4a94e45..580a74e9fb 100644 --- a/ext/psych/lib/psych/visitors/to_ruby.rb +++ b/ext/psych/lib/psych/visitors/to_ruby.rb @@ -96,11 +96,11 @@ module Psych Float(@ss.tokenize(o.value)) when "!ruby/regexp" klass = class_loader.regexp - o.value =~ /^\/(.*)\/([mixn]*)$/m - source = $1 + matches = /^\/(?<string>.*)\/(?<options>[mixn]*)$/m.match(o.value) + source = matches[:string].gsub('\/', '/') options = 0 lang = nil - $2&.each_char do |option| + matches[:options].each_char do |option| case option when 'x' then options |= Regexp::EXTENDED when 'i' then options |= Regexp::IGNORECASE @@ -197,6 +197,32 @@ module Psych s end + when /^!ruby\/data(-with-ivars)?(?::(.*))?$/ + data = register(o, resolve_class($2).allocate) if $2 + members = {} + + if $1 # data-with-ivars + ivars = {} + o.children.each_slice(2) do |type, vars| + case accept(type) + when 'members' + revive_data_members(members, vars) + data ||= allocate_anon_data(o, members) + when 'ivars' + revive_hash(ivars, vars) + end + end + ivars.each do |ivar, v| + data.instance_variable_set ivar, v + end + else + revive_data_members(members, o) + end + data ||= allocate_anon_data(o, members) + init_struct(data, **members) + data.freeze + data + when /^!ruby\/object:?(.*)?$/ name = $1 || 'Object' @@ -340,6 +366,20 @@ module Psych list end + def allocate_anon_data node, members + klass = class_loader.data.define(*members.keys) + register(node, klass.allocate) + end + + def revive_data_members hash, o + o.children.each_slice(2) do |k,v| + name = accept(k) + value = accept(v) + hash[class_loader.symbolize(name)] = value + end + hash + end + def revive_hash hash, o, tagged= false o.children.each_slice(2) { |k,v| key = accept(k) diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb index a9476df96e..d7958a8431 100644 --- a/ext/psych/lib/psych/visitors/yaml_tree.rb +++ b/ext/psych/lib/psych/visitors/yaml_tree.rb @@ -73,7 +73,7 @@ module Psych method = respond_to?(method) ? method : h[klass.superclass] - raise(TypeError, "Can't dump #{target.class}") unless method + raise(TypeError, "can't dump #{klass.name}") unless method h[klass] = method end.compare_by_identity @@ -162,6 +162,44 @@ module Psych alias :visit_Delegator :visit_Object + def visit_Data o + ivars = o.instance_variables + if ivars.empty? + tag = ['!ruby/data', o.class.name].compact.join(':') + register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK) + o.members.each do |member| + @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY + accept o.send member + end + @emitter.end_mapping + + else + tag = ['!ruby/data-with-ivars', o.class.name].compact.join(':') + node = @emitter.start_mapping(nil, tag, false, Psych::Nodes::Mapping::BLOCK) + register(o, node) + + # Dump the members + accept 'members' + @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK + o.members.each do |member| + @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY + accept o.send member + end + @emitter.end_mapping + + # Dump the ivars + accept 'ivars' + @emitter.start_mapping nil, nil, true, Nodes::Mapping::BLOCK + ivars.each do |ivar| + accept ivar.to_s + accept o.instance_variable_get ivar + end + @emitter.end_mapping + + @emitter.end_mapping + end + end + def visit_Struct o tag = ['!ruby/struct', o.class.name].compact.join(':') diff --git a/ext/psych/psych_to_ruby.c b/ext/psych/psych_to_ruby.c index ffe0c69c7f..d473a5f840 100644 --- a/ext/psych/psych_to_ruby.c +++ b/ext/psych/psych_to_ruby.c @@ -24,6 +24,15 @@ static VALUE path2class(VALUE self, VALUE path) return rb_path_to_class(path); } +static VALUE init_struct(VALUE self, VALUE data, VALUE attrs) +{ + VALUE args = rb_ary_new2(1); + rb_ary_push(args, attrs); + rb_struct_initialize(data, args); + + return data; +} + void Init_psych_to_ruby(void) { VALUE psych = rb_define_module("Psych"); @@ -33,6 +42,7 @@ void Init_psych_to_ruby(void) VALUE visitor = rb_define_class_under(visitors, "Visitor", rb_cObject); cPsychVisitorsToRuby = rb_define_class_under(visitors, "ToRuby", visitor); + rb_define_private_method(cPsychVisitorsToRuby, "init_struct", init_struct, 2); rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2); rb_define_private_method(class_loader, "path2class", path2class, 1); } diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c index 5a6446adb3..e094e2f55a 100644 --- a/ext/strscan/strscan.c +++ b/ext/strscan/strscan.c @@ -22,7 +22,7 @@ extern size_t onig_region_memsize(const struct re_registers *regs); #include <stdbool.h> -#define STRSCAN_VERSION "3.1.4.dev" +#define STRSCAN_VERSION "3.1.5.dev" /* ======================================================================= Data Type Definitions @@ -2211,7 +2211,10 @@ named_captures_iter(const OnigUChar *name, VALUE value = RUBY_Qnil; int i; for (i = 0; i < back_num; i++) { - value = strscan_aref(data->self, INT2NUM(back_refs[i])); + VALUE v = strscan_aref(data->self, INT2NUM(back_refs[i])); + if (!RB_NIL_P(v)) { + value = v; + } } rb_hash_aset(data->captures, key, value); return 0; diff --git a/include/ruby/atomic.h b/include/ruby/atomic.h index e0977d21aa..2f5090e62f 100644 --- a/include/ruby/atomic.h +++ b/include/ruby/atomic.h @@ -302,6 +302,19 @@ typedef unsigned int rb_atomic_t; RBIMPL_CAST(rbimpl_atomic_ptr_load((void **)&var)) /** +* 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_set((volatile void **)&(var), (val)) + +/** * 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. @@ -791,6 +804,19 @@ rbimpl_atomic_ptr_exchange(void *volatile *ptr, const void *val) RBIMPL_ATTR_ARTIFICIAL() RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL((1)) +static inline void +rbimpl_atomic_ptr_set(volatile void **ptr, void *val) +{ + 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_set(sptr, sval); +} + +RBIMPL_ATTR_ARTIFICIAL() +RBIMPL_ATTR_NOALIAS() +RBIMPL_ATTR_NONNULL((1)) static inline VALUE rbimpl_atomic_value_exchange(volatile VALUE *ptr, VALUE val) { diff --git a/include/ruby/internal/anyargs.h b/include/ruby/internal/anyargs.h index e3e1b6166d..e4c6d155cc 100644 --- a/include/ruby/internal/anyargs.h +++ b/include/ruby/internal/anyargs.h @@ -84,12 +84,15 @@ #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 */ @@ -348,6 +351,25 @@ RBIMPL_ANYARGS_DECL(rb_define_method, VALUE, const char *) #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 diff --git a/include/ruby/internal/attr/nonstring.h b/include/ruby/internal/attr/nonstring.h new file mode 100644 index 0000000000..de26e926d4 --- /dev/null +++ b/include/ruby/internal/attr/nonstring.h @@ -0,0 +1,32 @@ +#ifndef RBIMPL_ATTR_NONSTRING_H /*-*-C++-*-vi:se ft=cpp:*/ +#define RBIMPL_ATTR_NONSTRING_H +/** + * @file + * @author Ruby developers <[email protected]> + * @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)) +#else +# define RBIMPL_ATTR_NONSTRING() /* void */ +#endif + +#endif /* RBIMPL_ATTR_NONSTRING_H */ @@ -0,0 +1,423 @@ +// Glue code shared between YJIT and ZJIT for use from Rust. +// For FFI safety and bindgen compatibility reasons, certain types of C +// functions require wrapping before they can be called from Rust. Those show +// up here. +// +// Code specific to YJIT and ZJIT should go to yjit.c and zjit.c respectively. + +#include "internal.h" +#include "vm_core.h" +#include "vm_callinfo.h" +#include "builtin.h" +#include "insns.inc" +#include "insns_info.inc" +#include "iseq.h" +#include "internal/gc.h" + +unsigned int +rb_iseq_encoded_size(const rb_iseq_t *iseq) +{ + return iseq->body->iseq_size; +} + +// Get the PC for a given index in an iseq +VALUE * +rb_iseq_pc_at_idx(const rb_iseq_t *iseq, uint32_t insn_idx) +{ + RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq)); + RUBY_ASSERT_ALWAYS(insn_idx < iseq->body->iseq_size); + VALUE *encoded = iseq->body->iseq_encoded; + VALUE *pc = &encoded[insn_idx]; + return pc; +} + +// Get the opcode given a program counter. Can return trace opcode variants. +int +rb_iseq_opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc) +{ + // YJIT should only use iseqs after AST to bytecode compilation + RUBY_ASSERT_ALWAYS(FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)); + + const VALUE at_pc = *pc; + return rb_vm_insn_addr2opcode((const void *)at_pc); +} + +unsigned long +rb_RSTRING_LEN(VALUE str) +{ + return RSTRING_LEN(str); +} + +char * +rb_RSTRING_PTR(VALUE str) +{ + return RSTRING_PTR(str); +} + +const char * +rb_insn_name(VALUE insn) +{ + return insn_name(insn); +} + +unsigned int +rb_vm_ci_argc(const struct rb_callinfo *ci) +{ + return vm_ci_argc(ci); +} + +ID +rb_vm_ci_mid(const struct rb_callinfo *ci) +{ + return vm_ci_mid(ci); +} + +unsigned int +rb_vm_ci_flag(const struct rb_callinfo *ci) +{ + return vm_ci_flag(ci); +} + +const struct rb_callinfo_kwarg * +rb_vm_ci_kwarg(const struct rb_callinfo *ci) +{ + return vm_ci_kwarg(ci); +} + +int +rb_get_cikw_keyword_len(const struct rb_callinfo_kwarg *cikw) +{ + return cikw->keyword_len; +} + +VALUE +rb_get_cikw_keywords_idx(const struct rb_callinfo_kwarg *cikw, int idx) +{ + return cikw->keywords[idx]; +} + +rb_method_visibility_t +rb_METHOD_ENTRY_VISI(const rb_callable_method_entry_t *me) +{ + return METHOD_ENTRY_VISI(me); +} + +rb_method_type_t +rb_get_cme_def_type(const rb_callable_method_entry_t *cme) +{ + if (UNDEFINED_METHOD_ENTRY_P(cme)) { + return VM_METHOD_TYPE_UNDEF; + } + else { + return cme->def->type; + } +} + +ID +rb_get_cme_def_body_attr_id(const rb_callable_method_entry_t *cme) +{ + return cme->def->body.attr.id; +} + +enum method_optimized_type +rb_get_cme_def_body_optimized_type(const rb_callable_method_entry_t *cme) +{ + return cme->def->body.optimized.type; +} + +unsigned int +rb_get_cme_def_body_optimized_index(const rb_callable_method_entry_t *cme) +{ + return cme->def->body.optimized.index; +} + +rb_method_cfunc_t * +rb_get_cme_def_body_cfunc(const rb_callable_method_entry_t *cme) +{ + return UNALIGNED_MEMBER_PTR(cme->def, body.cfunc); +} + +uintptr_t +rb_get_def_method_serial(const rb_method_definition_t *def) +{ + return def->method_serial; +} + +ID +rb_get_def_original_id(const rb_method_definition_t *def) +{ + return def->original_id; +} + +int +rb_get_mct_argc(const rb_method_cfunc_t *mct) +{ + return mct->argc; +} + +void * +rb_get_mct_func(const rb_method_cfunc_t *mct) +{ + return (void*)(uintptr_t)mct->func; // this field is defined as type VALUE (*func)(ANYARGS) +} + +const rb_iseq_t * +rb_get_def_iseq_ptr(rb_method_definition_t *def) +{ + return def_iseq_ptr(def); +} + +const rb_iseq_t * +rb_get_iseq_body_local_iseq(const rb_iseq_t *iseq) +{ + return iseq->body->local_iseq; +} + +unsigned int +rb_get_iseq_body_local_table_size(const rb_iseq_t *iseq) +{ + return iseq->body->local_table_size; +} + +VALUE * +rb_get_iseq_body_iseq_encoded(const rb_iseq_t *iseq) +{ + return iseq->body->iseq_encoded; +} + +unsigned +rb_get_iseq_body_stack_max(const rb_iseq_t *iseq) +{ + return iseq->body->stack_max; +} + +enum rb_iseq_type +rb_get_iseq_body_type(const rb_iseq_t *iseq) +{ + return iseq->body->type; +} + +bool +rb_get_iseq_flags_has_lead(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.has_lead; +} + +bool +rb_get_iseq_flags_has_opt(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.has_opt; +} + +bool +rb_get_iseq_flags_has_kw(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.has_kw; +} + +bool +rb_get_iseq_flags_has_post(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.has_post; +} + +bool +rb_get_iseq_flags_has_kwrest(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.has_kwrest; +} + +bool +rb_get_iseq_flags_anon_kwrest(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.anon_kwrest; +} + +bool +rb_get_iseq_flags_has_rest(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.has_rest; +} + +bool +rb_get_iseq_flags_ruby2_keywords(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.ruby2_keywords; +} + +bool +rb_get_iseq_flags_has_block(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.has_block; +} + +bool +rb_get_iseq_flags_ambiguous_param0(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.ambiguous_param0; +} + +bool +rb_get_iseq_flags_accepts_no_kwarg(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.accepts_no_kwarg; +} + +bool +rb_get_iseq_flags_forwardable(const rb_iseq_t *iseq) +{ + return iseq->body->param.flags.forwardable; +} + +// This is defined only as a named struct inside rb_iseq_constant_body. +// By giving it a separate typedef, we make it nameable by rust-bindgen. +// Bindgen's temp/anon name isn't guaranteed stable. +typedef struct rb_iseq_param_keyword rb_iseq_param_keyword_struct; + +const rb_iseq_param_keyword_struct * +rb_get_iseq_body_param_keyword(const rb_iseq_t *iseq) +{ + return iseq->body->param.keyword; +} + +unsigned +rb_get_iseq_body_param_size(const rb_iseq_t *iseq) +{ + return iseq->body->param.size; +} + +int +rb_get_iseq_body_param_lead_num(const rb_iseq_t *iseq) +{ + return iseq->body->param.lead_num; +} + +int +rb_get_iseq_body_param_opt_num(const rb_iseq_t *iseq) +{ + return iseq->body->param.opt_num; +} + +const VALUE * +rb_get_iseq_body_param_opt_table(const rb_iseq_t *iseq) +{ + return iseq->body->param.opt_table; +} + +struct rb_control_frame_struct * +rb_get_ec_cfp(const rb_execution_context_t *ec) +{ + return ec->cfp; +} + +const rb_iseq_t * +rb_get_cfp_iseq(struct rb_control_frame_struct *cfp) +{ + return cfp->iseq; +} + +VALUE * +rb_get_cfp_pc(struct rb_control_frame_struct *cfp) +{ + return (VALUE*)cfp->pc; +} + +VALUE * +rb_get_cfp_sp(struct rb_control_frame_struct *cfp) +{ + return cfp->sp; +} + +VALUE +rb_get_cfp_self(struct rb_control_frame_struct *cfp) +{ + return cfp->self; +} + +VALUE * +rb_get_cfp_ep(struct rb_control_frame_struct *cfp) +{ + return (VALUE*)cfp->ep; +} + +const VALUE * +rb_get_cfp_ep_level(struct rb_control_frame_struct *cfp, uint32_t lv) +{ + uint32_t i; + const VALUE *ep = (VALUE*)cfp->ep; + for (i = 0; i < lv; i++) { + ep = VM_ENV_PREV_EP(ep); + } + return ep; +} + +VALUE +rb_yarv_class_of(VALUE obj) +{ + return rb_class_of(obj); +} + +// The FL_TEST() macro +VALUE +rb_FL_TEST(VALUE obj, VALUE flags) +{ + return RB_FL_TEST(obj, flags); +} + +// The FL_TEST_RAW() macro, normally an internal implementation detail +VALUE +rb_FL_TEST_RAW(VALUE obj, VALUE flags) +{ + return FL_TEST_RAW(obj, flags); +} + +// The RB_TYPE_P macro +bool +rb_RB_TYPE_P(VALUE obj, enum ruby_value_type t) +{ + return RB_TYPE_P(obj, t); +} + +long +rb_RSTRUCT_LEN(VALUE st) +{ + return RSTRUCT_LEN(st); +} + +const struct rb_callinfo * +rb_get_call_data_ci(const struct rb_call_data *cd) +{ + return cd->ci; +} + +bool +rb_BASIC_OP_UNREDEFINED_P(enum ruby_basic_operators bop, uint32_t klass) +{ + return BASIC_OP_UNREDEFINED_P(bop, klass); +} + +VALUE +rb_RCLASS_ORIGIN(VALUE c) +{ + return RCLASS_ORIGIN(c); +} + +// For debug builds +void +rb_assert_iseq_handle(VALUE handle) +{ + RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_iseq)); +} + +int +rb_IMEMO_TYPE_P(VALUE imemo, enum imemo_type imemo_type) +{ + return IMEMO_TYPE_P(imemo, imemo_type); +} + +void +rb_assert_cme_handle(VALUE handle) +{ + RUBY_ASSERT_ALWAYS(!rb_objspace_garbage_object_p(handle)); + RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_ment)); +} diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index d3176d4564..8d9a9b2d35 100644 --- a/lib/rubygems/gemcutter_utilities.rb +++ b/lib/rubygems/gemcutter_utilities.rb @@ -263,7 +263,10 @@ module Gem::GemcutterUtilities port = server.addr[1].to_s url_with_port = "#{webauthn_url}?port=#{port}" - say "You have enabled multi-factor authentication. Please visit #{url_with_port} to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option." + say "You have enabled multi-factor authentication. Please visit the following URL to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin command with the `--otp [your_code]` option." + say "" + say url_with_port + say "" threads = [WebauthnListener.listener_thread(host, server), WebauthnPoller.poll_thread(options, host, webauthn_url, credentials)] otp_thread = wait_for_otp_thread(*threads) @@ -40,6 +40,7 @@ #include "ruby/util.h" #include "builtin.h" #include "shape.h" +#include "ruby/internal/attr/nonstring.h" #define BITSPERSHORT (2*CHAR_BIT) #define SHORTMASK ((1<<BITSPERSHORT)-1) @@ -1515,7 +1516,7 @@ name_equal(const char *name, size_t nlen, const char *p, long l) static int sym2encidx(VALUE sym, VALUE val) { - static const char name_encoding[8] = "encoding"; + RBIMPL_ATTR_NONSTRING() static const char name_encoding[8] = "encoding"; const char *p; long l; if (rb_enc_get_index(sym) != ENCINDEX_US_ASCII) return -1; @@ -39,7 +39,8 @@ static void ASSERT_ractor_unlocking(rb_ractor_t *r) { #if RACTOR_CHECK_MODE > 0 - if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) { + const rb_execution_context_t *ec = rb_current_ec_noinline(); + if (ec != NULL && r->sync.locked_by == rb_ractor_self(rb_ec_ractor_ptr(ec))) { rb_bug("recursive ractor locking"); } #endif @@ -49,7 +50,8 @@ static void ASSERT_ractor_locking(rb_ractor_t *r) { #if RACTOR_CHECK_MODE > 0 - if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) { + const rb_execution_context_t *ec = rb_current_ec_noinline(); + if (ec != NULL && r->sync.locked_by != rb_ractor_self(rb_ec_ractor_ptr(ec))) { rp(r->sync.locked_by); rb_bug("ractor lock is not acquired."); } @@ -77,7 +79,7 @@ ractor_lock(rb_ractor_t *r, const char *file, int line) static void ractor_lock_self(rb_ractor_t *cr, const char *file, int line) { - VM_ASSERT(cr == GET_RACTOR()); + VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline())); #if RACTOR_CHECK_MODE > 0 VM_ASSERT(cr->sync.locked_by != cr->pub.self); #endif @@ -99,7 +101,7 @@ ractor_unlock(rb_ractor_t *r, const char *file, int line) static void ractor_unlock_self(rb_ractor_t *cr, const char *file, int line) { - VM_ASSERT(cr == GET_RACTOR()); + VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline())); #if RACTOR_CHECK_MODE > 0 VM_ASSERT(cr->sync.locked_by == cr->pub.self); #endif @@ -118,6 +118,9 @@ typedef struct { typedef struct { short int len; +#if defined(__has_attribute) && __has_attribute(nonstring) + __attribute__((nonstring)) +#endif const UChar name[6]; int ctype; } PosixBracketEntryType; @@ -499,13 +499,26 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo *variation_created = false; + // Fast path: if the shape has a single child, we can check it without a lock + struct rb_id_table *edges = RUBY_ATOMIC_PTR_LOAD(shape->edges); + if (edges && SINGLE_CHILD_P(edges)) { + rb_shape_t *child = SINGLE_CHILD(edges); + if (child->edge_name == id) { + return child; + } + } + RB_VM_LOCK_ENTER(); { + // The situation may have changed while we waited for the lock. + // So we load the edge again. + edges = RUBY_ATOMIC_PTR_LOAD(shape->edges); + // If the current shape has children - if (shape->edges) { + if (edges) { // Check if it only has one child - if (SINGLE_CHILD_P(shape->edges)) { - rb_shape_t *child = SINGLE_CHILD(shape->edges); + if (SINGLE_CHILD_P(edges)) { + rb_shape_t *child = SINGLE_CHILD(edges); // If the one child has a matching edge name, then great, // we found what we want. if (child->edge_name == id) { @@ -515,7 +528,7 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo else { // If it has more than one child, do a hash lookup to find it. VALUE lookup_result; - if (rb_id_table_lookup(shape->edges, id, &lookup_result)) { + if (rb_id_table_lookup(edges, id, &lookup_result)) { res = (rb_shape_t *)lookup_result; } } @@ -531,22 +544,26 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo else { rb_shape_t *new_shape = rb_shape_alloc_new_child(id, shape, shape_type); - if (!shape->edges) { + if (!edges) { // If the shape had no edge yet, we can directly set the new child - shape->edges = TAG_SINGLE_CHILD(new_shape); + edges = TAG_SINGLE_CHILD(new_shape); } else { // If the edge was single child we need to allocate a table. if (SINGLE_CHILD_P(shape->edges)) { - rb_shape_t *old_child = SINGLE_CHILD(shape->edges); - shape->edges = rb_id_table_create(2); - rb_id_table_insert(shape->edges, old_child->edge_name, (VALUE)old_child); + rb_shape_t *old_child = SINGLE_CHILD(edges); + edges = rb_id_table_create(2); + rb_id_table_insert(edges, old_child->edge_name, (VALUE)old_child); } - rb_id_table_insert(shape->edges, new_shape->edge_name, (VALUE)new_shape); + rb_id_table_insert(edges, new_shape->edge_name, (VALUE)new_shape); *variation_created = true; } + // We must use an atomic when setting the edges to ensure the writes + // from rb_shape_alloc_new_child are committed. + RUBY_ATOMIC_PTR_SET(shape->edges, edges); + res = new_shape; } } @@ -45,6 +45,7 @@ #include "ruby_atomic.h" #include "vm_core.h" #include "ractor_core.h" +#include "ruby/internal/attr/nonstring.h" #ifdef NEED_RUBY_ATOMIC_OPS rb_atomic_t @@ -976,7 +977,7 @@ check_reserved_signal_(const char *name, size_t name_len, int signo) if (prev) { ssize_t RB_UNUSED_VAR(err); static const int stderr_fd = 2; -#define NOZ(name, str) name[sizeof(str)-1] = str +#define NOZ(name, str) RBIMPL_ATTR_NONSTRING() name[sizeof(str)-1] = str static const char NOZ(msg1, " received in "); static const char NOZ(msg2, " handler\n"); @@ -140,6 +140,9 @@ xor64_to(uint64_t *v, const uint64_t s) #endif static const union { +#if defined(__has_attribute) && __has_attribute(nonstring) + __attribute__((nonstring)) +#endif char bin[32]; uint64_t u64[4]; } sip_init_state_bin = {"uespemos""modnarod""arenegyl""setybdet"}; @@ -46,6 +46,7 @@ #include "ruby/util.h" #include "ruby_assert.h" #include "vm_sync.h" +#include "ruby/internal/attr/nonstring.h" #if defined HAVE_CRYPT_R # if defined HAVE_CRYPT_H @@ -2346,9 +2347,14 @@ rb_str_with_debug_created_info(VALUE str, VALUE path, int line) } /* + * The documentation block below uses an include (instead of inline text) + * because the included text has non-ASCII characters (which are not allowed in a C file). + */ + +/* * * call-seq: - * String.new(string = '', **opts) -> new_string + * String.new(string = ''.encode(Encoding::ASCII_8BIT) , **options) -> new_string * * :include: doc/string/new.rdoc * @@ -11971,7 +11977,7 @@ enc_str_scrub(rb_encoding *enc, VALUE str, VALUE repl, int cr) encidx = rb_enc_to_index(enc); #define DEFAULT_REPLACE_CHAR(str) do { \ - static const char replace[sizeof(str)-1] = str; \ + RBIMPL_ATTR_NONSTRING() static const char replace[sizeof(str)-1] = str; \ rep = replace; replen = (int)sizeof(replace); \ } while (0) @@ -22,6 +22,7 @@ #include "symbol.h" #include "vm_sync.h" #include "builtin.h" +#include "ruby/internal/attr/nonstring.h" #if defined(USE_SYMBOL_GC) && !(USE_SYMBOL_GC+0) # undef USE_SYMBOL_GC diff --git a/template/Makefile.in b/template/Makefile.in index 7e88f94c7a..743971abcb 100644 --- a/template/Makefile.in +++ b/template/Makefile.in @@ -102,6 +102,7 @@ USE_RUBYGEMS = @USE_RUBYGEMS@ USE_RUBYGEMS_ = $(USE_RUBYGEMS:yes=) CPPFLAGS = @CPPFLAGS@ $(USE_RUBYGEMS_:no=-DDISABLE_RUBYGEMS=1) TOP_BUILD_DIR=@abs_top_builddir@ +JIT_OBJ=@JIT_OBJ@ YJIT_SUPPORT=@YJIT_SUPPORT@ YJIT_LIBS=@YJIT_LIBS@ YJIT_OBJ=@YJIT_OBJ@ diff --git a/template/id.c.tmpl b/template/id.c.tmpl index 5b9e879730..5aa8e47ce7 100644 --- a/template/id.c.tmpl +++ b/template/id.c.tmpl @@ -22,7 +22,8 @@ ops = ids[:token_op].uniq {|id, op, token| token && op} static const struct { unsigned short token; - const char name[3], term; + RBIMPL_ATTR_NONSTRING() const char name[3]; + const char term; } op_tbl[] = { % ops.each do |_id, op, token| % next unless token diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl index 675973b913..04f65ec5e3 100644 --- a/template/prelude.c.tmpl +++ b/template/prelude.c.tmpl @@ -88,6 +88,7 @@ Prelude.new(output, ARGV, vpath).instance_eval do #include "internal/ruby_parser.h" #include "internal/warnings.h" #include "iseq.h" +#include "ruby/internal/attr/nonstring.h" #include "ruby/ruby.h" #include "vm_core.h" @@ -107,12 +108,12 @@ static const struct { % size += line.size % next % end - char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=n%> */ + RBIMPL_ATTR_NONSTRING() char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=n%> */ % size = line.size % beg = n % } % if size > 0 - char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=lines.size+1%> */ + RBIMPL_ATTR_NONSTRING() char L<%=beg%><%=%>[<%=size%><%=%>]; /* <%=beg+1%>..<%=lines.size+1%> */ % end } prelude_code<%=i%><%=%> = { % size = 0 diff --git a/test/.excludes-mmtk/TestEtc.rb b/test/.excludes-mmtk/TestEtc.rb new file mode 100644 index 0000000000..746f5ba321 --- /dev/null +++ b/test/.excludes-mmtk/TestEtc.rb @@ -0,0 +1 @@ +exclude(:test_ractor_parallel, "glibc error: Mutex lock with MarkSweep debug") diff --git a/test/.excludes-mmtk/TestObjSpace.rb b/test/.excludes-mmtk/TestObjSpace.rb index 200faced19..05666e46f0 100644 --- a/test/.excludes-mmtk/TestObjSpace.rb +++ b/test/.excludes-mmtk/TestObjSpace.rb @@ -1,4 +1,3 @@ exclude(:test_dump_all_full, "testing behaviour specific to default GC") exclude(:test_dump_flag_age, "testing behaviour specific to default GC") exclude(:test_dump_flags, "testing behaviour specific to default GC") -exclude(:test_finalizer, "times out in debug mode on Ubuntu") diff --git a/test/.excludes-mmtk/TestObjectSpace.rb b/test/.excludes-mmtk/TestObjectSpace.rb new file mode 100644 index 0000000000..a92be8090c --- /dev/null +++ b/test/.excludes-mmtk/TestObjectSpace.rb @@ -0,0 +1 @@ +exclude(:test_finalizer, "times out in debug mode on Ubuntu") diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 39fa72e7dd..326cf22e1f 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -203,8 +203,9 @@ class TestObjSpace < Test::Unit::TestCase assert_equal(line1, ObjectSpace.allocation_sourceline(o1)) assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o1)) assert_equal(c1, ObjectSpace.allocation_generation(o1)) - assert_equal(self.class.name, ObjectSpace.allocation_class_path(o1)) - assert_equal(__method__, ObjectSpace.allocation_method_id(o1)) + # These assertions fail under coverage measurement: https://bugs.ruby-lang.org/issues/21298 + #assert_equal(self.class.name, ObjectSpace.allocation_class_path(o1)) + #assert_equal(__method__, ObjectSpace.allocation_method_id(o1)) assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o2)) assert_equal(line2, ObjectSpace.allocation_sourceline(o2)) diff --git a/test/psych/test_data.rb b/test/psych/test_data.rb new file mode 100644 index 0000000000..a67a037b9e --- /dev/null +++ b/test/psych/test_data.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true +require_relative 'helper' + +class PsychDataWithIvar < Data.define(:foo) + attr_reader :bar + def initialize(**) + @bar = 'hello' + super + end +end unless RUBY_VERSION < "3.2" + +module Psych + class TestData < TestCase + class SelfReferentialData < Data.define(:foo) + attr_accessor :ref + def initialize(foo:) + @ref = self + super + end + end unless RUBY_VERSION < "3.2" + + def setup + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + end + + # TODO: move to another test? + def test_dump_data + assert_equal <<~eoyml, Psych.dump(PsychDataWithIvar["bar"]) + --- !ruby/data-with-ivars:PsychDataWithIvar + members: + foo: bar + ivars: + "@bar": hello + eoyml + end + + def test_self_referential_data + circular = SelfReferentialData.new("foo") + + loaded = Psych.unsafe_load(Psych.dump(circular)) + assert_instance_of(SelfReferentialData, loaded.ref) + + assert_equal(circular, loaded) + assert_same(loaded, loaded.ref) + end + + def test_roundtrip + thing = PsychDataWithIvar.new("bar") + data = Psych.unsafe_load(Psych.dump(thing)) + + assert_equal "hello", data.bar + assert_equal "bar", data.foo + end + + def test_load + obj = Psych.unsafe_load(<<~eoyml) + --- !ruby/data-with-ivars:PsychDataWithIvar + members: + foo: bar + ivars: + "@bar": hello + eoyml + + assert_equal "hello", obj.bar + assert_equal "bar", obj.foo + end + end +end + diff --git a/test/psych/test_object_references.rb b/test/psych/test_object_references.rb index 86bb9034b9..0498d54eec 100644 --- a/test/psych/test_object_references.rb +++ b/test/psych/test_object_references.rb @@ -31,6 +31,11 @@ module Psych assert_reference_trip Struct.new(:foo).new(1) end + def test_data_has_references + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + assert_reference_trip Data.define(:foo).new(1) + end + def assert_reference_trip obj yml = Psych.dump([obj, obj]) assert_match(/\*-?\d+/, yml) diff --git a/test/psych/test_psych_set.rb b/test/psych/test_psych_set.rb new file mode 100644 index 0000000000..c72cd73f18 --- /dev/null +++ b/test/psych/test_psych_set.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true +require_relative 'helper' + +module Psych + class TestPsychSet < TestCase + def setup + super + @set = Psych::Set.new + @set['foo'] = 'bar' + @set['bar'] = 'baz' + end + + def test_dump + assert_match(/!set/, Psych.dump(@set)) + end + + def test_roundtrip + assert_cycle(@set) + end + + ### + # FIXME: Syck should also support !!set as shorthand + def test_load_from_yaml + loaded = Psych.unsafe_load(<<-eoyml) +--- !set +foo: bar +bar: baz + eoyml + assert_equal(@set, loaded) + end + + def test_loaded_class + assert_instance_of(Psych::Set, Psych.unsafe_load(Psych.dump(@set))) + end + + def test_set_shorthand + loaded = Psych.unsafe_load(<<-eoyml) +--- !!set +foo: bar +bar: baz + eoyml + assert_instance_of(Psych::Set, loaded) + end + + def test_set_self_reference + @set['self'] = @set + assert_cycle(@set) + end + + def test_stringify_names + @set[:symbol] = :value + + assert_match(/^:symbol: :value/, Psych.dump(@set)) + assert_match(/^symbol: :value/, Psych.dump(@set, stringify_names: true)) + end + end +end diff --git a/test/psych/test_safe_load.rb b/test/psych/test_safe_load.rb index a9ed737528..e6ca1e142b 100644 --- a/test/psych/test_safe_load.rb +++ b/test/psych/test_safe_load.rb @@ -114,6 +114,38 @@ module Psych end end + D = Data.define(:d) unless RUBY_VERSION < "3.2" + + def test_data_depends_on_sym + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + assert_safe_cycle(D.new(nil), permitted_classes: [D, Symbol]) + assert_raise(Psych::DisallowedClass) do + cycle D.new(nil), permitted_classes: [D] + end + end + + def test_anon_data + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + assert Psych.safe_load(<<-eoyml, permitted_classes: [Data, Symbol]) +--- !ruby/data + foo: bar + eoyml + + assert_raise(Psych::DisallowedClass) do + Psych.safe_load(<<-eoyml, permitted_classes: [Data]) +--- !ruby/data + foo: bar + eoyml + end + + assert_raise(Psych::DisallowedClass) do + Psych.safe_load(<<-eoyml, permitted_classes: [Symbol]) +--- !ruby/data + foo: bar + eoyml + end + end + def test_safe_load_default_fallback assert_nil Psych.safe_load("") end diff --git a/test/psych/test_serialize_subclasses.rb b/test/psych/test_serialize_subclasses.rb index 344c79b3ef..640c331337 100644 --- a/test/psych/test_serialize_subclasses.rb +++ b/test/psych/test_serialize_subclasses.rb @@ -35,5 +35,23 @@ module Psych so = StructSubclass.new('foo', [1,2,3]) assert_equal so, Psych.unsafe_load(Psych.dump(so)) end + + class DataSubclass < Data.define(:foo) + def initialize(foo:) + @bar = "hello #{foo}" + super(foo: foo) + end + + def == other + super(other) && @bar == other.instance_eval{ @bar } + end + end unless RUBY_VERSION < "3.2" + + def test_data_subclass + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + so = DataSubclass.new('foo') + assert_equal so, Psych.unsafe_load(Psych.dump(so)) + end + end end diff --git a/test/psych/test_set.rb b/test/psych/test_set.rb index b4968d3425..ccd591c626 100644 --- a/test/psych/test_set.rb +++ b/test/psych/test_set.rb @@ -1,57 +1,36 @@ +# encoding: UTF-8 # frozen_string_literal: true require_relative 'helper' +require 'set' unless defined?(Set) module Psych class TestSet < TestCase def setup - super - @set = Psych::Set.new - @set['foo'] = 'bar' - @set['bar'] = 'baz' + @set = ::Set.new([1, 2, 3]) end def test_dump - assert_match(/!set/, Psych.dump(@set)) + assert_equal <<~YAML, Psych.dump(@set) + --- !ruby/object:Set + hash: + 1: true + 2: true + 3: true + YAML end - def test_roundtrip - assert_cycle(@set) - end - - ### - # FIXME: Syck should also support !!set as shorthand - def test_load_from_yaml - loaded = Psych.unsafe_load(<<-eoyml) ---- !set -foo: bar -bar: baz - eoyml - assert_equal(@set, loaded) + def test_load + assert_equal @set, Psych.load(<<~YAML, permitted_classes: [::Set]) + --- !ruby/object:Set + hash: + 1: true + 2: true + 3: true + YAML end - def test_loaded_class - assert_instance_of(Psych::Set, Psych.unsafe_load(Psych.dump(@set))) - end - - def test_set_shorthand - loaded = Psych.unsafe_load(<<-eoyml) ---- !!set -foo: bar -bar: baz - eoyml - assert_instance_of(Psych::Set, loaded) - end - - def test_set_self_reference - @set['self'] = @set - assert_cycle(@set) - end - - def test_stringify_names - @set[:symbol] = :value - - assert_match(/^:symbol: :value/, Psych.dump(@set)) - assert_match(/^symbol: :value/, Psych.dump(@set, stringify_names: true)) + def test_roundtrip + assert_equal @set, Psych.load(Psych.dump(@set), permitted_classes: [::Set]) end end end diff --git a/test/psych/test_yaml.rb b/test/psych/test_yaml.rb index 897a7c8935..134c346c90 100644 --- a/test/psych/test_yaml.rb +++ b/test/psych/test_yaml.rb @@ -6,6 +6,7 @@ require_relative 'helper' # [ruby-core:01946] module Psych_Tests StructTest = Struct::new( :c ) + DataTest = Data.define( :c ) unless RUBY_VERSION < "3.2" end class Psych_Unit_Tests < Psych::TestCase @@ -35,6 +36,10 @@ class Psych_Unit_Tests < Psych::TestCase assert_cycle(Regexp.new("foo\nbar")) end + def test_regexp_with_slash + assert_cycle(Regexp.new('/')) + end + # [ruby-core:34969] def test_regexp_with_n assert_cycle(Regexp.new('',Regexp::NOENCODING)) @@ -1037,7 +1042,6 @@ EOY end def test_ruby_struct - Struct.send(:remove_const, :MyBookStruct) if Struct.const_defined?(:MyBookStruct) # Ruby structures book_struct = Struct::new( "MyBookStruct", :author, :title, :year, :isbn ) assert_to_yaml( @@ -1069,6 +1073,47 @@ EOY c: 123 EOY + ensure + Struct.__send__(:remove_const, :MyBookStruct) if book_struct + end + + def test_ruby_data + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + # Ruby Data value objects + book_class = Data.define(:author, :title, :year, :isbn) + Object.const_set(:MyBookData, book_class) + assert_to_yaml( + [ book_class.new( "Yukihiro Matsumoto", "Ruby in a Nutshell", 2002, "0-596-00214-9" ), + book_class.new( [ 'Dave Thomas', 'Andy Hunt' ], "The Pickaxe", 2002, + book_class.new( "This should be the ISBN", "but I have more data here", 2002, "None" ) + ) + ], <<EOY +- !ruby/data:MyBookData + author: Yukihiro Matsumoto + title: Ruby in a Nutshell + year: 2002 + isbn: 0-596-00214-9 +- !ruby/data:MyBookData + author: + - Dave Thomas + - Andy Hunt + title: The Pickaxe + year: 2002 + isbn: !ruby/data:MyBookData + author: This should be the ISBN + title: but I have more data here + year: 2002 + isbn: None +EOY + ) + + assert_to_yaml( Psych_Tests::DataTest.new( 123 ), <<EOY ) +--- !ruby/data:Psych_Tests::DataTest +c: 123 +EOY + + ensure + Object.__send__(:remove_const, :MyBookData) if book_class end def test_ruby_rational diff --git a/test/psych/visitors/test_yaml_tree.rb b/test/psych/visitors/test_yaml_tree.rb index 01e685134a..bd3919f83d 100644 --- a/test/psych/visitors/test_yaml_tree.rb +++ b/test/psych/visitors/test_yaml_tree.rb @@ -73,6 +73,27 @@ module Psych assert_equal s.method, obj.method end + D = Data.define(:foo) unless RUBY_VERSION < "3.2" + + def test_data + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + assert_cycle D.new('bar') + end + + def test_data_anon + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + d = Data.define(:foo).new('bar') + obj = Psych.unsafe_load(Psych.dump(d)) + assert_equal d.foo, obj.foo + end + + def test_data_override_method + omit "Data requires ruby >= 3.2" if RUBY_VERSION < "3.2" + d = Data.define(:method).new('override') + obj = Psych.unsafe_load(Psych.dump(d)) + assert_equal d.method, obj.method + end + def test_exception ex = Exception.new 'foo' loaded = Psych.unsafe_load(Psych.dump(ex)) diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index 032f78f6a8..86c1f51dde 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -92,7 +92,7 @@ class TestISeq < Test::Unit::TestCase 42 end EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_forwardable @@ -102,7 +102,7 @@ class TestISeq < Test::Unit::TestCase def foo(...); bar(...); end } EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval.new.foo(40, 2)) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval.new.foo(40, 2)) end def test_super_with_block @@ -112,7 +112,7 @@ class TestISeq < Test::Unit::TestCase end 42 EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_super_with_block_hash_0 @@ -123,7 +123,7 @@ class TestISeq < Test::Unit::TestCase end 42 EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_super_with_block_and_kwrest @@ -133,7 +133,7 @@ class TestISeq < Test::Unit::TestCase end 42 EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_lambda_with_ractor_roundtrip @@ -143,7 +143,7 @@ class TestISeq < Test::Unit::TestCase Ractor.make_shareable(y) y.call EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_super_with_anonymous_block @@ -153,7 +153,7 @@ class TestISeq < Test::Unit::TestCase end 42 EOF - assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval) + assert_equal(42, ISeq.load_from_binary(iseq_to_binary(iseq)).eval) end def test_ractor_unshareable_outer_variable @@ -182,7 +182,7 @@ class TestISeq < Test::Unit::TestCase # shareable_constant_value: literal REGEX = /#{}/ # [Bug #20569] RUBY - assert_includes iseq.to_binary, "REGEX".b + assert_includes iseq_to_binary(iseq), "REGEX".b end def test_disasm_encoding @@ -566,16 +566,20 @@ class TestISeq < Test::Unit::TestCase } end + def iseq_to_binary(iseq) + iseq.to_binary + rescue RuntimeError => e + omit e.message if /compile with coverage/ =~ e.message + raise + end + def assert_iseq_to_binary(code, mesg = nil) iseq = RubyVM::InstructionSequence.compile(code) bin = assert_nothing_raised(mesg) do - iseq.to_binary - rescue RuntimeError => e - omit e.message if /compile with coverage/ =~ e.message - raise + iseq_to_binary(iseq) end 10.times do - bin2 = iseq.to_binary + bin2 = iseq_to_binary(iseq) assert_equal(bin, bin2, message(mesg) {diff hexdump(bin), hexdump(bin2)}) end iseq2 = RubyVM::InstructionSequence.load_from_binary(bin) @@ -593,7 +597,7 @@ class TestISeq < Test::Unit::TestCase def test_to_binary_with_hidden_local_variables assert_iseq_to_binary("for _foo in bar; end") - bin = RubyVM::InstructionSequence.compile(<<-RUBY).to_binary + bin = iseq_to_binary(RubyVM::InstructionSequence.compile(<<-RUBY)) Object.new.instance_eval do a = [] def self.bar; [1] end @@ -668,7 +672,7 @@ class TestISeq < Test::Unit::TestCase end RUBY - iseq_bin = iseq.to_binary + iseq_bin = iseq_to_binary(iseq) iseq = ISeq.load_from_binary(iseq_bin) lines = [] TracePoint.new(tracepoint_type){|tp| @@ -764,7 +768,7 @@ class TestISeq < Test::Unit::TestCase def test_iseq_builtin_load Tempfile.create(["builtin", ".iseq"]) do |f| f.binmode - f.write(RubyVM::InstructionSequence.of(1.method(:abs)).to_binary) + f.write(iseq_to_binary(RubyVM::InstructionSequence.of(1.method(:abs)))) f.close assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~'end;'}") begin; @@ -857,7 +861,7 @@ class TestISeq < Test::Unit::TestCase def test_loading_kwargs_memory_leak assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~'end;'}", rss: true) - a = RubyVM::InstructionSequence.compile("foo(bar: :baz)").to_binary + a = iseq_to_binary(RubyVM::InstructionSequence.compile("foo(bar: :baz)")) begin; 1_000_000.times do RubyVM::InstructionSequence.load_from_binary(a) @@ -868,7 +872,7 @@ class TestISeq < Test::Unit::TestCase def test_ibf_bignum iseq = RubyVM::InstructionSequence.compile("0x0"+"_0123_4567_89ab_cdef"*5) expected = iseq.eval - result = RubyVM::InstructionSequence.load_from_binary(iseq.to_binary).eval + result = RubyVM::InstructionSequence.load_from_binary(iseq_to_binary(iseq)).eval assert_equal expected, result, proc {sprintf("expected: %x, result: %x", expected, result)} end diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb index bc4f13ff2a..ac18699736 100644 --- a/test/rubygems/test_gem_commands_owner_command.rb +++ b/test/rubygems/test_gem_commands_owner_command.rb @@ -386,9 +386,10 @@ EOF end end - assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output + assert_match @stub_fetcher.webauthn_url_with_port(server.port), @stub_ui.output assert_match "You are verified with a security device. You may close the browser window.", @stub_ui.output assert_equal "Uvh6T57tkWuUnWYo", @stub_fetcher.last_request["OTP"] assert_match response_success, @stub_ui.output @@ -413,9 +414,10 @@ EOF end assert_match @stub_fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key - assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @stub_ui.output + assert_match @stub_fetcher.webauthn_url_with_port(server.port), @stub_ui.output assert_match "ERROR: Security device verification failed: Something went wrong", @stub_ui.error refute_match "You are verified with a security device. You may close the browser window.", @stub_ui.output refute_match response_success, @stub_ui.output @@ -435,9 +437,10 @@ EOF end end - assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ "command with the `--otp [your_code]` option.", @stub_ui.output + assert_match @stub_fetcher.webauthn_url_with_port(server.port), @stub_ui.output assert_match "You are verified with a security device. You may close the browser window.", @stub_ui.output assert_equal "Uvh6T57tkWuUnWYo", @stub_fetcher.last_request["OTP"] assert_match response_success, @stub_ui.output @@ -463,9 +466,10 @@ EOF end assert_match @stub_fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key - assert_match "You have enabled multi-factor authentication. Please visit #{@stub_fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ "command with the `--otp [your_code]` option.", @stub_ui.output + assert_match @stub_fetcher.webauthn_url_with_port(server.port), @stub_ui.output assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \ "or been used already.", @stub_ui.error refute_match "You are verified with a security device. You may close the browser window.", @stub_ui.output diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb index 2d0190b49f..bedc8e0d58 100644 --- a/test/rubygems/test_gem_commands_push_command.rb +++ b/test/rubygems/test_gem_commands_push_command.rb @@ -477,9 +477,10 @@ class TestGemCommandsPushCommand < Gem::TestCase end end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @ui.output assert_match "You are verified with a security device. You may close the browser window.", @ui.output assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"] assert_match response_success, @ui.output @@ -505,9 +506,10 @@ class TestGemCommandsPushCommand < Gem::TestCase assert_equal 1, error.exit_code assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @ui.output assert_match "ERROR: Security device verification failed: Something went wrong", @ui.error refute_match "You are verified with a security device. You may close the browser window.", @ui.output refute_match response_success, @ui.output @@ -527,9 +529,10 @@ class TestGemCommandsPushCommand < Gem::TestCase end end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @ui.output assert_match "You are verified with a security device. You may close the browser window.", @ui.output assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"] assert_match response_success, @ui.output @@ -553,9 +556,10 @@ class TestGemCommandsPushCommand < Gem::TestCase assert_equal 1, error.exit_code assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ - "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, you can re-run the gem signin " \ - "command with the `--otp [your_code]` option.", @ui.output + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ + "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ + "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @ui.output assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \ "or been used already.", @ui.error refute_match "You are verified with a security device. You may close the browser window.", @ui.output diff --git a/test/rubygems/test_gem_commands_yank_command.rb b/test/rubygems/test_gem_commands_yank_command.rb index eb78e3a542..213f098374 100644 --- a/test/rubygems/test_gem_commands_yank_command.rb +++ b/test/rubygems/test_gem_commands_yank_command.rb @@ -131,9 +131,10 @@ class TestGemCommandsYankCommand < Gem::TestCase end assert_match %r{Yanking gem from http://example}, @ui.output - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @ui.output assert_match "You are verified with a security device. You may close the browser window.", @ui.output assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"] assert_match "Successfully yanked", @ui.output @@ -163,9 +164,10 @@ class TestGemCommandsYankCommand < Gem::TestCase assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key assert_match %r{Yanking gem from http://example}, @ui.output - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @ui.output assert_match "ERROR: Security device verification failed: Something went wrong", @ui.error refute_match "You are verified with a security device. You may close the browser window.", @ui.output refute_match "Successfully yanked", @ui.output @@ -189,9 +191,10 @@ class TestGemCommandsYankCommand < Gem::TestCase end assert_match %r{Yanking gem from http://example}, @ui.output - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @ui.output assert_match "You are verified with a security device. You may close the browser window.", @ui.output assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"] assert_match "Successfully yanked", @ui.output @@ -219,9 +222,10 @@ class TestGemCommandsYankCommand < Gem::TestCase assert_match @fetcher.last_request["Authorization"], Gem.configuration.rubygems_api_key assert_match %r{Yanking gem from http://example}, @ui.output - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @ui.output assert_match "ERROR: Security device verification failed: The token in the link you used has either expired " \ "or been used already.", @ui.error refute_match "You are verified with a security device. You may close the browser window.", @ui.output diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb index a3236e6276..9204dc5f20 100644 --- a/test/rubygems/test_gem_gemcutter_utilities.rb +++ b/test/rubygems/test_gem_gemcutter_utilities.rb @@ -233,9 +233,10 @@ class TestGemGemcutterUtilities < Gem::TestCase end end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @sign_in_ui.output assert_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"] end @@ -255,9 +256,10 @@ class TestGemGemcutterUtilities < Gem::TestCase end assert_equal 1, error.exit_code - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @sign_in_ui.output assert_match "ERROR: Security device verification failed: Something went wrong", @sign_in_ui.error refute_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output refute_match "Signed in with API key:", @sign_in_ui.output @@ -273,9 +275,10 @@ class TestGemGemcutterUtilities < Gem::TestCase util_sign_in end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @sign_in_ui.output assert_match "You are verified with a security device. You may close the browser window.", @sign_in_ui.output assert_equal "Uvh6T57tkWuUnWYo", @fetcher.last_request["OTP"] end @@ -292,9 +295,10 @@ class TestGemGemcutterUtilities < Gem::TestCase end end - assert_match "You have enabled multi-factor authentication. Please visit #{@fetcher.webauthn_url_with_port(server.port)} " \ + assert_match "You have enabled multi-factor authentication. Please visit the following URL " \ "to authenticate via security device. If you can't verify using WebAuthn but have OTP enabled, " \ "you can re-run the gem signin command with the `--otp [your_code]` option.", @sign_in_ui.output + assert_match @fetcher.webauthn_url_with_port(server.port), @sign_in_ui.output assert_match "ERROR: Security device verification failed: " \ "The token in the link you used has either expired or been used already.", @sign_in_ui.error end diff --git a/test/strscan/test_stringscanner.rb b/test/strscan/test_stringscanner.rb index 0a28d92368..e895a8382b 100644 --- a/test/strscan/test_stringscanner.rb +++ b/test/strscan/test_stringscanner.rb @@ -967,6 +967,12 @@ module StringScannerTests assert_equal({}, scan.named_captures) end + def test_named_captures_same_name_union + scan = StringScanner.new("123") + assert_equal(1, scan.match?(/(?<number>0)|(?<number>1)|(?<number>2)/)) + assert_equal({"number" => "1"}, scan.named_captures) + end + def test_scan_integer s = create_string_scanner('abc') assert_equal(3, s.match?(/(?<a>abc)/)) # set named_captures diff --git a/tool/ruby_vm/views/insns_info.inc.erb b/tool/ruby_vm/views/insns_info.inc.erb index 6ba12a856e..0a6f71fee3 100644 --- a/tool/ruby_vm/views/insns_info.inc.erb +++ b/tool/ruby_vm/views/insns_info.inc.erb @@ -11,6 +11,8 @@ this_file: 'contains instruction information for yarv instruction sequence.', edit: __FILE__, } %> +#ifndef INSNS_INFO_INC +#define INSNS_INFO_INC 1 <%= render 'insn_type_chars' %> <%= render 'insn_name_info' %> <%= render 'insn_len_info' %> @@ -20,3 +22,4 @@ <%= render 'zjit_helpers' %> <%= render 'attributes' %> <%= render 'comptime_insn_stack_increase' %> +#endif diff --git a/tool/sync_default_gems.rb b/tool/sync_default_gems.rb index 903baf49dd..5b0bca72b7 100755 --- a/tool/sync_default_gems.rb +++ b/tool/sync_default_gems.rb @@ -46,7 +46,6 @@ module SyncDefaultGems resolv: "ruby/resolv", rubygems: 'rubygems/rubygems', securerandom: "ruby/securerandom", - set: "ruby/set", shellwords: "ruby/shellwords", singleton: "ruby/singleton", stringio: 'ruby/stringio', @@ -295,9 +294,6 @@ module SyncDefaultGems cp_r("#{upstream}/test/digest", "test") cp_r("#{upstream}/digest.gemspec", "ext/digest") `git checkout ext/digest/depend ext/digest/*/depend` - when "set" - sync_lib gem, upstream - cp_r(Dir.glob("#{upstream}/test/*"), "test/set") when "optparse" sync_lib gem, upstream rm_rf(%w[doc/optparse]) diff --git a/tool/test-coverage.rb b/tool/test-coverage.rb index 055577feea..28ef0bf7f8 100644 --- a/tool/test-coverage.rb +++ b/tool/test-coverage.rb @@ -114,6 +114,10 @@ pid = $$ pwd = Dir.pwd at_exit do + # Some tests leave GC.stress enabled, causing slow coverage processing. + # Reset it here to avoid performance issues. + GC.stress = false + exit_exc = $! Dir.chdir(pwd) do @@ -2118,7 +2118,7 @@ rb_vm_check_ints(rb_execution_context_t *ec) VM_ASSERT(ruby_assert_critical_section_entered == 0); #endif - VM_ASSERT(ec == GET_EC()); + VM_ASSERT(ec == rb_current_ec_noinline()); if (UNLIKELY(RUBY_VM_INTERRUPTED_ANY(ec))) { rb_threadptr_execute_interrupts(rb_ec_thread_ptr(ec), 0); diff --git a/vm_method.c b/vm_method.c index 22adc469f7..7288fdd2ec 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1371,7 +1371,6 @@ prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_ if (me->defined_class == 0) { RB_DEBUG_COUNTER_INC(mc_cme_complement); VM_ASSERT_TYPE2(defined_class, T_ICLASS, T_MODULE); - VM_ASSERT(me->defined_class == 0, "me->defined_class: %s", rb_obj_info(me->defined_class)); mtbl = RCLASS_CALLABLE_M_TBL(defined_class); diff --git a/wasm/setjmp.h b/wasm/setjmp.h index e65bfc0ca0..82cfff1d00 100644 --- a/wasm/setjmp.h +++ b/wasm/setjmp.h @@ -5,7 +5,7 @@ #include <stdbool.h> #ifndef WASM_SETJMP_STACK_BUFFER_SIZE -# define WASM_SETJMP_STACK_BUFFER_SIZE 6144 +# define WASM_SETJMP_STACK_BUFFER_SIZE 8192 #endif struct __rb_wasm_asyncify_jmp_buf { @@ -396,12 +396,6 @@ rb_full_cfunc_return(rb_execution_context_t *ec, VALUE return_value) ec->cfp->sp++; } -unsigned int -rb_iseq_encoded_size(const rb_iseq_t *iseq) -{ - return iseq->body->iseq_size; -} - // TODO(alan): consider using an opaque pointer for the payload rather than a void pointer void * rb_iseq_get_yjit_payload(const rb_iseq_t *iseq) @@ -437,40 +431,6 @@ rb_iseq_reset_jit_func(const rb_iseq_t *iseq) iseq->body->jit_exception_calls = 0; } -// Get the PC for a given index in an iseq -VALUE * -rb_iseq_pc_at_idx(const rb_iseq_t *iseq, uint32_t insn_idx) -{ - RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq)); - RUBY_ASSERT_ALWAYS(insn_idx < iseq->body->iseq_size); - VALUE *encoded = iseq->body->iseq_encoded; - VALUE *pc = &encoded[insn_idx]; - return pc; -} - -// Get the opcode given a program counter. Can return trace opcode variants. -int -rb_iseq_opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc) -{ - // YJIT should only use iseqs after AST to bytecode compilation - RUBY_ASSERT_ALWAYS(FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)); - - const VALUE at_pc = *pc; - return rb_vm_insn_addr2opcode((const void *)at_pc); -} - -unsigned long -rb_RSTRING_LEN(VALUE str) -{ - return RSTRING_LEN(str); -} - -char * -rb_RSTRING_PTR(VALUE str) -{ - return RSTRING_PTR(str); -} - rb_proc_t * rb_yjit_get_proc_ptr(VALUE procv) { @@ -484,121 +444,8 @@ rb_yjit_get_proc_ptr(VALUE procv) // Bindgen's temp/anon name isn't guaranteed stable. typedef struct rb_iseq_param_keyword rb_seq_param_keyword_struct; -const char * -rb_insn_name(VALUE insn) -{ - return insn_name(insn); -} - -unsigned int -rb_vm_ci_argc(const struct rb_callinfo *ci) -{ - return vm_ci_argc(ci); -} - -ID -rb_vm_ci_mid(const struct rb_callinfo *ci) -{ - return vm_ci_mid(ci); -} - -unsigned int -rb_vm_ci_flag(const struct rb_callinfo *ci) -{ - return vm_ci_flag(ci); -} - -const struct rb_callinfo_kwarg * -rb_vm_ci_kwarg(const struct rb_callinfo *ci) -{ - return vm_ci_kwarg(ci); -} - -int -rb_get_cikw_keyword_len(const struct rb_callinfo_kwarg *cikw) -{ - return cikw->keyword_len; -} - -VALUE -rb_get_cikw_keywords_idx(const struct rb_callinfo_kwarg *cikw, int idx) -{ - return cikw->keywords[idx]; -} - -rb_method_visibility_t -rb_METHOD_ENTRY_VISI(const rb_callable_method_entry_t *me) -{ - return METHOD_ENTRY_VISI(me); -} - -rb_method_type_t -rb_get_cme_def_type(const rb_callable_method_entry_t *cme) -{ - if (UNDEFINED_METHOD_ENTRY_P(cme)) { - return VM_METHOD_TYPE_UNDEF; - } - else { - return cme->def->type; - } -} - -ID -rb_get_cme_def_body_attr_id(const rb_callable_method_entry_t *cme) -{ - return cme->def->body.attr.id; -} - ID rb_get_symbol_id(VALUE namep); -enum method_optimized_type -rb_get_cme_def_body_optimized_type(const rb_callable_method_entry_t *cme) -{ - return cme->def->body.optimized.type; -} - -unsigned int -rb_get_cme_def_body_optimized_index(const rb_callable_method_entry_t *cme) -{ - return cme->def->body.optimized.index; -} - -rb_method_cfunc_t * -rb_get_cme_def_body_cfunc(const rb_callable_method_entry_t *cme) -{ - return UNALIGNED_MEMBER_PTR(cme->def, body.cfunc); -} - -uintptr_t -rb_get_def_method_serial(const rb_method_definition_t *def) -{ - return def->method_serial; -} - -ID -rb_get_def_original_id(const rb_method_definition_t *def) -{ - return def->original_id; -} - -int -rb_get_mct_argc(const rb_method_cfunc_t *mct) -{ - return mct->argc; -} - -void * -rb_get_mct_func(const rb_method_cfunc_t *mct) -{ - return (void*)(uintptr_t)mct->func; // this field is defined as type VALUE (*func)(ANYARGS) -} - -const rb_iseq_t * -rb_get_def_iseq_ptr(rb_method_definition_t *def) -{ - return def_iseq_ptr(def); -} - VALUE rb_get_def_bmethod_proc(rb_method_definition_t *def) { @@ -607,143 +454,11 @@ rb_get_def_bmethod_proc(rb_method_definition_t *def) } const rb_iseq_t * -rb_get_iseq_body_local_iseq(const rb_iseq_t *iseq) -{ - return iseq->body->local_iseq; -} - -const rb_iseq_t * rb_get_iseq_body_parent_iseq(const rb_iseq_t *iseq) { return iseq->body->parent_iseq; } -unsigned int -rb_get_iseq_body_local_table_size(const rb_iseq_t *iseq) -{ - return iseq->body->local_table_size; -} - -VALUE * -rb_get_iseq_body_iseq_encoded(const rb_iseq_t *iseq) -{ - return iseq->body->iseq_encoded; -} - -unsigned -rb_get_iseq_body_stack_max(const rb_iseq_t *iseq) -{ - return iseq->body->stack_max; -} - -enum rb_iseq_type -rb_get_iseq_body_type(const rb_iseq_t *iseq) -{ - return iseq->body->type; -} - -bool -rb_get_iseq_flags_has_lead(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_lead; -} - -bool -rb_get_iseq_flags_has_opt(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_opt; -} - -bool -rb_get_iseq_flags_has_kw(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_kw; -} - -bool -rb_get_iseq_flags_has_post(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_post; -} - -bool -rb_get_iseq_flags_has_kwrest(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_kwrest; -} - -bool -rb_get_iseq_flags_anon_kwrest(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.anon_kwrest; -} - -bool -rb_get_iseq_flags_has_rest(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_rest; -} - -bool -rb_get_iseq_flags_ruby2_keywords(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.ruby2_keywords; -} - -bool -rb_get_iseq_flags_has_block(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_block; -} - -bool -rb_get_iseq_flags_ambiguous_param0(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.ambiguous_param0; -} - -bool -rb_get_iseq_flags_accepts_no_kwarg(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.accepts_no_kwarg; -} - -bool -rb_get_iseq_flags_forwardable(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.forwardable; -} - -const rb_seq_param_keyword_struct * -rb_get_iseq_body_param_keyword(const rb_iseq_t *iseq) -{ - return iseq->body->param.keyword; -} - -unsigned -rb_get_iseq_body_param_size(const rb_iseq_t *iseq) -{ - return iseq->body->param.size; -} - -int -rb_get_iseq_body_param_lead_num(const rb_iseq_t *iseq) -{ - return iseq->body->param.lead_num; -} - -int -rb_get_iseq_body_param_opt_num(const rb_iseq_t *iseq) -{ - return iseq->body->param.opt_num; -} - -const VALUE * -rb_get_iseq_body_param_opt_table(const rb_iseq_t *iseq) -{ - return iseq->body->param.opt_table; -} - VALUE rb_optimized_call(VALUE *recv, rb_execution_context_t *ec, int argc, VALUE *argv, int kw_splat, VALUE block_handler) { @@ -789,30 +504,6 @@ rb_yjit_str_simple_append(VALUE str1, VALUE str2) return rb_str_cat(str1, RSTRING_PTR(str2), RSTRING_LEN(str2)); } -struct rb_control_frame_struct * -rb_get_ec_cfp(const rb_execution_context_t *ec) -{ - return ec->cfp; -} - -const rb_iseq_t * -rb_get_cfp_iseq(struct rb_control_frame_struct *cfp) -{ - return cfp->iseq; -} - -VALUE * -rb_get_cfp_pc(struct rb_control_frame_struct *cfp) -{ - return (VALUE*)cfp->pc; -} - -VALUE * -rb_get_cfp_sp(struct rb_control_frame_struct *cfp) -{ - return cfp->sp; -} - void rb_set_cfp_pc(struct rb_control_frame_struct *cfp, const VALUE *pc) { @@ -825,37 +516,8 @@ rb_set_cfp_sp(struct rb_control_frame_struct *cfp, VALUE *sp) cfp->sp = sp; } -VALUE -rb_get_cfp_self(struct rb_control_frame_struct *cfp) -{ - return cfp->self; -} - -VALUE * -rb_get_cfp_ep(struct rb_control_frame_struct *cfp) -{ - return (VALUE*)cfp->ep; -} - -const VALUE * -rb_get_cfp_ep_level(struct rb_control_frame_struct *cfp, uint32_t lv) -{ - uint32_t i; - const VALUE *ep = (VALUE*)cfp->ep; - for (i = 0; i < lv; i++) { - ep = VM_ENV_PREV_EP(ep); - } - return ep; -} - extern VALUE *rb_vm_base_ptr(struct rb_control_frame_struct *cfp); -VALUE -rb_yarv_class_of(VALUE obj) -{ - return rb_class_of(obj); -} - // YJIT needs this function to never allocate and never raise VALUE rb_yarv_str_eql_internal(VALUE str1, VALUE str2) @@ -989,33 +651,6 @@ rb_yjit_iseq_inspect(const rb_iseq_t *iseq) return buf; } -// The FL_TEST() macro -VALUE -rb_FL_TEST(VALUE obj, VALUE flags) -{ - return RB_FL_TEST(obj, flags); -} - -// The FL_TEST_RAW() macro, normally an internal implementation detail -VALUE -rb_FL_TEST_RAW(VALUE obj, VALUE flags) -{ - return FL_TEST_RAW(obj, flags); -} - -// The RB_TYPE_P macro -bool -rb_RB_TYPE_P(VALUE obj, enum ruby_value_type t) -{ - return RB_TYPE_P(obj, t); -} - -long -rb_RSTRUCT_LEN(VALUE st) -{ - return RSTRUCT_LEN(st); -} - // There are RSTRUCT_SETs in ruby/internal/core/rstruct.h and internal/struct.h // with different types (int vs long) for k. Here we use the one from ruby/internal/core/rstruct.h, // which takes an int. @@ -1025,24 +660,6 @@ rb_RSTRUCT_SET(VALUE st, int k, VALUE v) RSTRUCT_SET(st, k, v); } -const struct rb_callinfo * -rb_get_call_data_ci(const struct rb_call_data *cd) -{ - return cd->ci; -} - -bool -rb_BASIC_OP_UNREDEFINED_P(enum ruby_basic_operators bop, uint32_t klass) -{ - return BASIC_OP_UNREDEFINED_P(bop, klass); -} - -VALUE -rb_RCLASS_ORIGIN(VALUE c) -{ - return RCLASS_ORIGIN(c); -} - // Return the string encoding index int rb_ENCODING_GET(VALUE obj) @@ -1056,32 +673,12 @@ rb_yjit_multi_ractor_p(void) return rb_multi_ractor_p(); } -// For debug builds -void -rb_assert_iseq_handle(VALUE handle) -{ - RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_iseq)); -} - -int -rb_IMEMO_TYPE_P(VALUE imemo, enum imemo_type imemo_type) -{ - return IMEMO_TYPE_P(imemo, imemo_type); -} - bool rb_yjit_constcache_shareable(const struct iseq_inline_constant_cache_entry *ice) { return (ice->flags & IMEMO_CONST_CACHE_SHAREABLE) != 0; } -void -rb_assert_cme_handle(VALUE handle) -{ - RUBY_ASSERT_ALWAYS(!rb_objspace_garbage_object_p(handle)); - RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_ment)); -} - // Used for passing a callback and other data over rb_objspace_each_objects struct iseq_callback_data { rb_iseq_callback callback; diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 9305a50cc9..9ee6fc7b8f 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -47,6 +47,7 @@ fn main() { // Our C file for glue code .header(src_root.join("yjit.c").to_str().unwrap()) + .header(src_root.join("jit.c").to_str().unwrap()) // Don't want to copy over C comment .generate_comments(false) diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index db4f75e39b..490a61226a 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -979,6 +979,7 @@ pub type robject_offsets = u32; pub const RUBY_OFFSET_RSTRING_LEN: rstring_offsets = 16; pub type rstring_offsets = u32; pub type rb_seq_param_keyword_struct = rb_iseq_constant_body__bindgen_ty_1_rb_iseq_param_keyword; +pub type rb_iseq_param_keyword_struct = rb_iseq_constant_body__bindgen_ty_1_rb_iseq_param_keyword; extern "C" { pub fn ruby_xfree(ptr: *mut ::std::os::raw::c_void); pub fn rb_class_attached_object(klass: VALUE) -> VALUE; @@ -1163,15 +1164,78 @@ extern "C" { pub fn rb_yjit_reserve_addr_space(mem_size: u32) -> *mut u8; pub fn rb_c_method_tracing_currently_enabled(ec: *const rb_execution_context_t) -> bool; pub fn rb_full_cfunc_return(ec: *mut rb_execution_context_t, return_value: VALUE); - pub fn rb_iseq_encoded_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_iseq_get_yjit_payload(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_void; pub fn rb_iseq_set_yjit_payload(iseq: *const rb_iseq_t, payload: *mut ::std::os::raw::c_void); pub fn rb_iseq_reset_jit_func(iseq: *const rb_iseq_t); + pub fn rb_yjit_get_proc_ptr(procv: VALUE) -> *mut rb_proc_t; + pub fn rb_get_symbol_id(namep: VALUE) -> ID; + pub fn rb_get_def_bmethod_proc(def: *mut rb_method_definition_t) -> VALUE; + pub fn rb_get_iseq_body_parent_iseq(iseq: *const rb_iseq_t) -> *const rb_iseq_t; + pub fn rb_optimized_call( + recv: *mut VALUE, + ec: *mut rb_execution_context_t, + argc: ::std::os::raw::c_int, + argv: *mut VALUE, + kw_splat: ::std::os::raw::c_int, + block_handler: VALUE, + ) -> VALUE; + pub fn rb_yjit_iseq_builtin_attrs(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; + pub fn rb_yjit_builtin_function(iseq: *const rb_iseq_t) -> *const rb_builtin_function; + pub fn rb_yjit_str_simple_append(str1: VALUE, str2: VALUE) -> VALUE; + pub fn rb_set_cfp_pc(cfp: *mut rb_control_frame_struct, pc: *const VALUE); + pub fn rb_set_cfp_sp(cfp: *mut rb_control_frame_struct, sp: *mut VALUE); + pub fn rb_vm_base_ptr(cfp: *mut rb_control_frame_struct) -> *mut VALUE; + pub fn rb_yarv_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE; + pub fn rb_str_neq_internal(str1: VALUE, str2: VALUE) -> VALUE; + pub fn rb_yarv_ary_entry_internal(ary: VALUE, offset: ::std::os::raw::c_long) -> VALUE; + pub fn rb_ary_unshift_m(argc: ::std::os::raw::c_int, argv: *mut VALUE, ary: VALUE) -> VALUE; + pub fn rb_yjit_rb_ary_subseq_length(ary: VALUE, beg: ::std::os::raw::c_long) -> VALUE; + pub fn rb_yjit_fix_div_fix(recv: VALUE, obj: VALUE) -> VALUE; + pub fn rb_yjit_fix_mod_fix(recv: VALUE, obj: VALUE) -> VALUE; + pub fn rb_yjit_ruby2_keywords_splat_p(obj: VALUE) -> usize; + pub fn rb_yjit_splat_varg_checks( + sp: *mut VALUE, + splat_array: VALUE, + cfp: *mut rb_control_frame_t, + ) -> VALUE; + pub fn rb_yjit_splat_varg_cfunc(stack_splat_array: *mut VALUE) -> ::std::os::raw::c_int; + pub fn rb_yjit_dump_iseq_loc(iseq: *const rb_iseq_t, insn_idx: u32); + pub fn rb_yjit_iseq_inspect(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_char; + pub fn rb_RSTRUCT_SET(st: VALUE, k: ::std::os::raw::c_int, v: VALUE); + pub fn rb_ENCODING_GET(obj: VALUE) -> ::std::os::raw::c_int; + pub fn rb_yjit_multi_ractor_p() -> bool; + pub fn rb_yjit_constcache_shareable(ice: *const iseq_inline_constant_cache_entry) -> bool; + pub fn rb_yjit_for_each_iseq(callback: rb_iseq_callback, data: *mut ::std::os::raw::c_void); + pub fn rb_yjit_obj_written( + old: VALUE, + young: VALUE, + file: *const ::std::os::raw::c_char, + line: ::std::os::raw::c_int, + ); + pub fn rb_yjit_vm_lock_then_barrier( + recursive_lock_level: *mut ::std::os::raw::c_uint, + file: *const ::std::os::raw::c_char, + line: ::std::os::raw::c_int, + ); + pub fn rb_yjit_vm_unlock( + recursive_lock_level: *mut ::std::os::raw::c_uint, + file: *const ::std::os::raw::c_char, + line: ::std::os::raw::c_int, + ); + pub fn rb_object_shape_count() -> VALUE; + pub fn rb_yjit_assert_holding_vm_lock(); + pub fn rb_yjit_sendish_sp_pops(ci: *const rb_callinfo) -> usize; + pub fn rb_yjit_invokeblock_sp_pops(ci: *const rb_callinfo) -> usize; + pub fn rb_yjit_set_exception_return( + cfp: *mut rb_control_frame_t, + leave_exit: *mut ::std::os::raw::c_void, + leave_exception: *mut ::std::os::raw::c_void, + ); + pub fn rb_iseq_encoded_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_iseq_pc_at_idx(iseq: *const rb_iseq_t, insn_idx: u32) -> *mut VALUE; pub fn rb_iseq_opcode_at_pc(iseq: *const rb_iseq_t, pc: *const VALUE) -> ::std::os::raw::c_int; pub fn rb_RSTRING_LEN(str_: VALUE) -> ::std::os::raw::c_ulong; pub fn rb_RSTRING_PTR(str_: VALUE) -> *mut ::std::os::raw::c_char; - pub fn rb_yjit_get_proc_ptr(procv: VALUE) -> *mut rb_proc_t; pub fn rb_insn_name(insn: VALUE) -> *const ::std::os::raw::c_char; pub fn rb_vm_ci_argc(ci: *const rb_callinfo) -> ::std::os::raw::c_uint; pub fn rb_vm_ci_mid(ci: *const rb_callinfo) -> ID; @@ -1185,7 +1249,6 @@ extern "C" { pub fn rb_METHOD_ENTRY_VISI(me: *const rb_callable_method_entry_t) -> rb_method_visibility_t; pub fn rb_get_cme_def_type(cme: *const rb_callable_method_entry_t) -> rb_method_type_t; pub fn rb_get_cme_def_body_attr_id(cme: *const rb_callable_method_entry_t) -> ID; - pub fn rb_get_symbol_id(namep: VALUE) -> ID; pub fn rb_get_cme_def_body_optimized_type( cme: *const rb_callable_method_entry_t, ) -> method_optimized_type; @@ -1200,9 +1263,7 @@ extern "C" { pub fn rb_get_mct_argc(mct: *const rb_method_cfunc_t) -> ::std::os::raw::c_int; pub fn rb_get_mct_func(mct: *const rb_method_cfunc_t) -> *mut ::std::os::raw::c_void; pub fn rb_get_def_iseq_ptr(def: *mut rb_method_definition_t) -> *const rb_iseq_t; - pub fn rb_get_def_bmethod_proc(def: *mut rb_method_definition_t) -> VALUE; pub fn rb_get_iseq_body_local_iseq(iseq: *const rb_iseq_t) -> *const rb_iseq_t; - pub fn rb_get_iseq_body_parent_iseq(iseq: *const rb_iseq_t) -> *const rb_iseq_t; pub fn rb_get_iseq_body_local_table_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_get_iseq_body_iseq_encoded(iseq: *const rb_iseq_t) -> *mut VALUE; pub fn rb_get_iseq_body_stack_max(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; @@ -1221,87 +1282,27 @@ extern "C" { pub fn rb_get_iseq_flags_forwardable(iseq: *const rb_iseq_t) -> bool; pub fn rb_get_iseq_body_param_keyword( iseq: *const rb_iseq_t, - ) -> *const rb_seq_param_keyword_struct; + ) -> *const rb_iseq_param_keyword_struct; pub fn rb_get_iseq_body_param_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_get_iseq_body_param_lead_num(iseq: *const rb_iseq_t) -> ::std::os::raw::c_int; pub fn rb_get_iseq_body_param_opt_num(iseq: *const rb_iseq_t) -> ::std::os::raw::c_int; pub fn rb_get_iseq_body_param_opt_table(iseq: *const rb_iseq_t) -> *const VALUE; - pub fn rb_optimized_call( - recv: *mut VALUE, - ec: *mut rb_execution_context_t, - argc: ::std::os::raw::c_int, - argv: *mut VALUE, - kw_splat: ::std::os::raw::c_int, - block_handler: VALUE, - ) -> VALUE; - pub fn rb_yjit_iseq_builtin_attrs(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; - pub fn rb_yjit_builtin_function(iseq: *const rb_iseq_t) -> *const rb_builtin_function; - pub fn rb_yjit_str_simple_append(str1: VALUE, str2: VALUE) -> VALUE; pub fn rb_get_ec_cfp(ec: *const rb_execution_context_t) -> *mut rb_control_frame_struct; pub fn rb_get_cfp_iseq(cfp: *mut rb_control_frame_struct) -> *const rb_iseq_t; pub fn rb_get_cfp_pc(cfp: *mut rb_control_frame_struct) -> *mut VALUE; pub fn rb_get_cfp_sp(cfp: *mut rb_control_frame_struct) -> *mut VALUE; - pub fn rb_set_cfp_pc(cfp: *mut rb_control_frame_struct, pc: *const VALUE); - pub fn rb_set_cfp_sp(cfp: *mut rb_control_frame_struct, sp: *mut VALUE); pub fn rb_get_cfp_self(cfp: *mut rb_control_frame_struct) -> VALUE; pub fn rb_get_cfp_ep(cfp: *mut rb_control_frame_struct) -> *mut VALUE; pub fn rb_get_cfp_ep_level(cfp: *mut rb_control_frame_struct, lv: u32) -> *const VALUE; - pub fn rb_vm_base_ptr(cfp: *mut rb_control_frame_struct) -> *mut VALUE; pub fn rb_yarv_class_of(obj: VALUE) -> VALUE; - pub fn rb_yarv_str_eql_internal(str1: VALUE, str2: VALUE) -> VALUE; - pub fn rb_str_neq_internal(str1: VALUE, str2: VALUE) -> VALUE; - pub fn rb_yarv_ary_entry_internal(ary: VALUE, offset: ::std::os::raw::c_long) -> VALUE; - pub fn rb_ary_unshift_m(argc: ::std::os::raw::c_int, argv: *mut VALUE, ary: VALUE) -> VALUE; - pub fn rb_yjit_rb_ary_subseq_length(ary: VALUE, beg: ::std::os::raw::c_long) -> VALUE; - pub fn rb_yjit_fix_div_fix(recv: VALUE, obj: VALUE) -> VALUE; - pub fn rb_yjit_fix_mod_fix(recv: VALUE, obj: VALUE) -> VALUE; - pub fn rb_yjit_ruby2_keywords_splat_p(obj: VALUE) -> usize; - pub fn rb_yjit_splat_varg_checks( - sp: *mut VALUE, - splat_array: VALUE, - cfp: *mut rb_control_frame_t, - ) -> VALUE; - pub fn rb_yjit_splat_varg_cfunc(stack_splat_array: *mut VALUE) -> ::std::os::raw::c_int; - pub fn rb_yjit_dump_iseq_loc(iseq: *const rb_iseq_t, insn_idx: u32); - pub fn rb_yjit_iseq_inspect(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_char; pub fn rb_FL_TEST(obj: VALUE, flags: VALUE) -> VALUE; pub fn rb_FL_TEST_RAW(obj: VALUE, flags: VALUE) -> VALUE; pub fn rb_RB_TYPE_P(obj: VALUE, t: ruby_value_type) -> bool; pub fn rb_RSTRUCT_LEN(st: VALUE) -> ::std::os::raw::c_long; - pub fn rb_RSTRUCT_SET(st: VALUE, k: ::std::os::raw::c_int, v: VALUE); pub fn rb_get_call_data_ci(cd: *const rb_call_data) -> *const rb_callinfo; pub fn rb_BASIC_OP_UNREDEFINED_P(bop: ruby_basic_operators, klass: u32) -> bool; pub fn rb_RCLASS_ORIGIN(c: VALUE) -> VALUE; - pub fn rb_ENCODING_GET(obj: VALUE) -> ::std::os::raw::c_int; - pub fn rb_yjit_multi_ractor_p() -> bool; pub fn rb_assert_iseq_handle(handle: VALUE); pub fn rb_IMEMO_TYPE_P(imemo: VALUE, imemo_type: imemo_type) -> ::std::os::raw::c_int; - pub fn rb_yjit_constcache_shareable(ice: *const iseq_inline_constant_cache_entry) -> bool; pub fn rb_assert_cme_handle(handle: VALUE); - pub fn rb_yjit_for_each_iseq(callback: rb_iseq_callback, data: *mut ::std::os::raw::c_void); - pub fn rb_yjit_obj_written( - old: VALUE, - young: VALUE, - file: *const ::std::os::raw::c_char, - line: ::std::os::raw::c_int, - ); - pub fn rb_yjit_vm_lock_then_barrier( - recursive_lock_level: *mut ::std::os::raw::c_uint, - file: *const ::std::os::raw::c_char, - line: ::std::os::raw::c_int, - ); - pub fn rb_yjit_vm_unlock( - recursive_lock_level: *mut ::std::os::raw::c_uint, - file: *const ::std::os::raw::c_char, - line: ::std::os::raw::c_int, - ); - pub fn rb_object_shape_count() -> VALUE; - pub fn rb_yjit_assert_holding_vm_lock(); - pub fn rb_yjit_sendish_sp_pops(ci: *const rb_callinfo) -> usize; - pub fn rb_yjit_invokeblock_sp_pops(ci: *const rb_callinfo) -> usize; - pub fn rb_yjit_set_exception_return( - cfp: *mut rb_control_frame_t, - leave_exit: *mut ::std::os::raw::c_void, - leave_exception: *mut ::std::os::raw::c_void, - ); } @@ -156,18 +156,6 @@ rb_zjit_reserve_addr_space(uint32_t mem_size) #endif } -unsigned long -rb_RSTRING_LEN(VALUE str) -{ - return RSTRING_LEN(str); -} - -char * -rb_RSTRING_PTR(VALUE str) -{ - return RSTRING_PTR(str); -} - void rb_zjit_profile_disable(const rb_iseq_t *iseq); void @@ -189,374 +177,20 @@ rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit RB_VM_LOCK_LEAVE(); } -unsigned int -rb_iseq_encoded_size(const rb_iseq_t *iseq) -{ - return iseq->body->iseq_size; -} - -// Get the opcode given a program counter. Can return trace opcode variants. -int -rb_iseq_opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc) -{ - // ZJIT should only use iseqs after AST to bytecode compilation - RUBY_ASSERT_ALWAYS(FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)); - - const VALUE at_pc = *pc; - return rb_vm_insn_addr2opcode((const void *)at_pc); -} - -// Get the PC for a given index in an iseq -VALUE * -rb_iseq_pc_at_idx(const rb_iseq_t *iseq, uint32_t insn_idx) -{ - RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(iseq, imemo_iseq)); - RUBY_ASSERT_ALWAYS(insn_idx < iseq->body->iseq_size); - VALUE *encoded = iseq->body->iseq_encoded; - VALUE *pc = &encoded[insn_idx]; - return pc; -} - -const char * -rb_insn_name(VALUE insn) -{ - return insn_name(insn); -} - -struct rb_control_frame_struct * -rb_get_ec_cfp(const rb_execution_context_t *ec) -{ - return ec->cfp; -} - -const rb_iseq_t * -rb_get_cfp_iseq(struct rb_control_frame_struct *cfp) -{ - return cfp->iseq; -} - -VALUE * -rb_get_cfp_pc(struct rb_control_frame_struct *cfp) -{ - return (VALUE*)cfp->pc; -} - -VALUE * -rb_get_cfp_sp(struct rb_control_frame_struct *cfp) -{ - return cfp->sp; -} - -VALUE -rb_get_cfp_self(struct rb_control_frame_struct *cfp) -{ - return cfp->self; -} - -VALUE * -rb_get_cfp_ep(struct rb_control_frame_struct *cfp) -{ - return (VALUE*)cfp->ep; -} - -const VALUE * -rb_get_cfp_ep_level(struct rb_control_frame_struct *cfp, uint32_t lv) -{ - uint32_t i; - const VALUE *ep = (VALUE*)cfp->ep; - for (i = 0; i < lv; i++) { - ep = VM_ENV_PREV_EP(ep); - } - return ep; -} - extern VALUE *rb_vm_base_ptr(struct rb_control_frame_struct *cfp); -rb_method_type_t -rb_get_cme_def_type(const rb_callable_method_entry_t *cme) -{ - if (UNDEFINED_METHOD_ENTRY_P(cme)) { - return VM_METHOD_TYPE_UNDEF; - } - else { - return cme->def->type; - } -} - -ID -rb_get_cme_def_body_attr_id(const rb_callable_method_entry_t *cme) -{ - return cme->def->body.attr.id; -} - -enum method_optimized_type -rb_get_cme_def_body_optimized_type(const rb_callable_method_entry_t *cme) -{ - return cme->def->body.optimized.type; -} - -unsigned int -rb_get_cme_def_body_optimized_index(const rb_callable_method_entry_t *cme) -{ - return cme->def->body.optimized.index; -} - -rb_method_cfunc_t * -rb_get_cme_def_body_cfunc(const rb_callable_method_entry_t *cme) -{ - return UNALIGNED_MEMBER_PTR(cme->def, body.cfunc); -} - -uintptr_t -rb_get_def_method_serial(const rb_method_definition_t *def) -{ - return def->method_serial; -} - -ID -rb_get_def_original_id(const rb_method_definition_t *def) -{ - return def->original_id; -} - -int -rb_get_mct_argc(const rb_method_cfunc_t *mct) -{ - return mct->argc; -} - -void * -rb_get_mct_func(const rb_method_cfunc_t *mct) -{ - return (void*)(uintptr_t)mct->func; // this field is defined as type VALUE (*func)(ANYARGS) -} - -const rb_iseq_t * -rb_get_def_iseq_ptr(rb_method_definition_t *def) -{ - return def_iseq_ptr(def); -} - -const rb_iseq_t * -rb_get_iseq_body_local_iseq(const rb_iseq_t *iseq) -{ - return iseq->body->local_iseq; -} - -VALUE * -rb_get_iseq_body_iseq_encoded(const rb_iseq_t *iseq) -{ - return iseq->body->iseq_encoded; -} - -unsigned -rb_get_iseq_body_stack_max(const rb_iseq_t *iseq) -{ - return iseq->body->stack_max; -} - -enum rb_iseq_type -rb_get_iseq_body_type(const rb_iseq_t *iseq) -{ - return iseq->body->type; -} - -bool -rb_get_iseq_flags_has_lead(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_lead; -} - -bool -rb_get_iseq_flags_has_opt(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_opt; -} - -bool -rb_get_iseq_flags_has_kw(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_kw; -} - -bool -rb_get_iseq_flags_has_post(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_post; -} - -bool -rb_get_iseq_flags_has_kwrest(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_kwrest; -} - -bool -rb_get_iseq_flags_anon_kwrest(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.anon_kwrest; -} - -bool -rb_get_iseq_flags_has_rest(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_rest; -} - -bool -rb_get_iseq_flags_ruby2_keywords(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.ruby2_keywords; -} - -bool -rb_get_iseq_flags_has_block(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.has_block; -} - -bool -rb_get_iseq_flags_ambiguous_param0(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.ambiguous_param0; -} - -bool -rb_get_iseq_flags_accepts_no_kwarg(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.accepts_no_kwarg; -} - -bool -rb_get_iseq_flags_forwardable(const rb_iseq_t *iseq) -{ - return iseq->body->param.flags.forwardable; -} - -// This is defined only as a named struct inside rb_iseq_constant_body. -// By giving it a separate typedef, we make it nameable by rust-bindgen. -// Bindgen's temp/anon name isn't guaranteed stable. -typedef struct rb_iseq_param_keyword rb_iseq_param_keyword_struct; - -const rb_iseq_param_keyword_struct * -rb_get_iseq_body_param_keyword(const rb_iseq_t *iseq) -{ - return iseq->body->param.keyword; -} - -unsigned -rb_get_iseq_body_param_size(const rb_iseq_t *iseq) -{ - return iseq->body->param.size; -} - -int -rb_get_iseq_body_param_lead_num(const rb_iseq_t *iseq) -{ - return iseq->body->param.lead_num; -} - -int -rb_get_iseq_body_param_opt_num(const rb_iseq_t *iseq) -{ - return iseq->body->param.opt_num; -} - -const VALUE * -rb_get_iseq_body_param_opt_table(const rb_iseq_t *iseq) -{ - return iseq->body->param.opt_table; -} - -unsigned int -rb_get_iseq_body_local_table_size(const rb_iseq_t *iseq) -{ - return iseq->body->local_table_size; -} - -int -rb_get_cikw_keyword_len(const struct rb_callinfo_kwarg *cikw) -{ - return cikw->keyword_len; -} - -VALUE -rb_get_cikw_keywords_idx(const struct rb_callinfo_kwarg *cikw, int idx) -{ - return cikw->keywords[idx]; -} - -const struct rb_callinfo * -rb_get_call_data_ci(const struct rb_call_data *cd) -{ - return cd->ci; -} - -// The FL_TEST() macro -VALUE -rb_FL_TEST(VALUE obj, VALUE flags) -{ - return RB_FL_TEST(obj, flags); -} - -// The FL_TEST_RAW() macro, normally an internal implementation detail -VALUE -rb_FL_TEST_RAW(VALUE obj, VALUE flags) -{ - return FL_TEST_RAW(obj, flags); -} - -// The RB_TYPE_P macro -bool -rb_RB_TYPE_P(VALUE obj, enum ruby_value_type t) -{ - return RB_TYPE_P(obj, t); -} - -long -rb_RSTRUCT_LEN(VALUE st) -{ - return RSTRUCT_LEN(st); -} - -bool -rb_BASIC_OP_UNREDEFINED_P(enum ruby_basic_operators bop, uint32_t klass) -{ - return BASIC_OP_UNREDEFINED_P(bop, klass); -} - bool rb_zjit_multi_ractor_p(void) { return rb_multi_ractor_p(); } -// For debug builds -void -rb_assert_iseq_handle(VALUE handle) -{ - RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_iseq)); -} - bool rb_zjit_constcache_shareable(const struct iseq_inline_constant_cache_entry *ice) { return (ice->flags & IMEMO_CONST_CACHE_SHAREABLE) != 0; } -void -rb_assert_cme_handle(VALUE handle) -{ - RUBY_ASSERT_ALWAYS(!rb_objspace_garbage_object_p(handle)); - RUBY_ASSERT_ALWAYS(IMEMO_TYPE_P(handle, imemo_ment)); -} - -int -rb_IMEMO_TYPE_P(VALUE imemo, enum imemo_type imemo_type) -{ - return IMEMO_TYPE_P(imemo, imemo_type); -} - // Release the VM lock. The lock level must point to the same integer used to // acquire the lock. void @@ -614,42 +248,6 @@ rb_zjit_icache_invalidate(void *start, void *end) #endif } -unsigned int -rb_vm_ci_argc(const struct rb_callinfo *ci) -{ - return vm_ci_argc(ci); -} - -ID -rb_vm_ci_mid(const struct rb_callinfo *ci) -{ - return vm_ci_mid(ci); -} - -unsigned int -rb_vm_ci_flag(const struct rb_callinfo *ci) -{ - return vm_ci_flag(ci); -} - -const struct rb_callinfo_kwarg * -rb_vm_ci_kwarg(const struct rb_callinfo *ci) -{ - return vm_ci_kwarg(ci); -} - -rb_method_visibility_t -rb_METHOD_ENTRY_VISI(const rb_callable_method_entry_t *me) -{ - return METHOD_ENTRY_VISI(me); -} - -VALUE -rb_yarv_class_of(VALUE obj) -{ - return rb_class_of(obj); -} - // Acquire the VM lock and then signal all other Ruby threads (ractors) to // contend for the VM lock, putting them to sleep. ZJIT uses this to evict // threads running inside generated code so among other things, it can @@ -661,12 +259,6 @@ rb_zjit_vm_lock_then_barrier(unsigned int *recursive_lock_level, const char *fil rb_vm_barrier(); } -VALUE -rb_RCLASS_ORIGIN(VALUE c) -{ - return RCLASS_ORIGIN(c); -} - // Convert a given ISEQ's instructions to zjit_* instructions void rb_zjit_profile_enable(const rb_iseq_t *iseq) diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 5372ead3e5..f47dff744c 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -51,6 +51,7 @@ fn main() { // Our C file for glue code .header(src_root.join(c_file).to_str().unwrap()) + .header(src_root.join("jit.c").to_str().unwrap()) // Don't want to copy over C comment .generate_comments(false) diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 169789ef77..88532abb63 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -929,21 +929,46 @@ unsafe extern "C" { pub fn rb_jit_cont_each_iseq(callback: rb_iseq_callback, data: *mut ::std::os::raw::c_void); pub fn rb_zjit_get_page_size() -> u32; pub fn rb_zjit_reserve_addr_space(mem_size: u32) -> *mut u8; - pub fn rb_RSTRING_LEN(str_: VALUE) -> ::std::os::raw::c_ulong; - pub fn rb_RSTRING_PTR(str_: VALUE) -> *mut ::std::os::raw::c_char; pub fn rb_zjit_profile_disable(iseq: *const rb_iseq_t); + pub fn rb_vm_base_ptr(cfp: *mut rb_control_frame_struct) -> *mut VALUE; + pub fn rb_zjit_multi_ractor_p() -> bool; + pub fn rb_zjit_constcache_shareable(ice: *const iseq_inline_constant_cache_entry) -> bool; + pub fn rb_zjit_vm_unlock( + recursive_lock_level: *mut ::std::os::raw::c_uint, + file: *const ::std::os::raw::c_char, + line: ::std::os::raw::c_int, + ); + pub fn rb_zjit_mark_writable(mem_block: *mut ::std::os::raw::c_void, mem_size: u32) -> bool; + pub fn rb_zjit_mark_executable(mem_block: *mut ::std::os::raw::c_void, mem_size: u32); + pub fn rb_zjit_mark_unused(mem_block: *mut ::std::os::raw::c_void, mem_size: u32) -> bool; + pub fn rb_zjit_icache_invalidate( + start: *mut ::std::os::raw::c_void, + end: *mut ::std::os::raw::c_void, + ); + pub fn rb_zjit_vm_lock_then_barrier( + recursive_lock_level: *mut ::std::os::raw::c_uint, + file: *const ::std::os::raw::c_char, + line: ::std::os::raw::c_int, + ); + pub fn rb_iseq_get_zjit_payload(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_void; + pub fn rb_iseq_set_zjit_payload(iseq: *const rb_iseq_t, payload: *mut ::std::os::raw::c_void); + pub fn rb_zjit_print_exception(); pub fn rb_iseq_encoded_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; - pub fn rb_iseq_opcode_at_pc(iseq: *const rb_iseq_t, pc: *const VALUE) -> ::std::os::raw::c_int; pub fn rb_iseq_pc_at_idx(iseq: *const rb_iseq_t, insn_idx: u32) -> *mut VALUE; + pub fn rb_iseq_opcode_at_pc(iseq: *const rb_iseq_t, pc: *const VALUE) -> ::std::os::raw::c_int; + pub fn rb_RSTRING_LEN(str_: VALUE) -> ::std::os::raw::c_ulong; + pub fn rb_RSTRING_PTR(str_: VALUE) -> *mut ::std::os::raw::c_char; pub fn rb_insn_name(insn: VALUE) -> *const ::std::os::raw::c_char; - pub fn rb_get_ec_cfp(ec: *const rb_execution_context_t) -> *mut rb_control_frame_struct; - pub fn rb_get_cfp_iseq(cfp: *mut rb_control_frame_struct) -> *const rb_iseq_t; - pub fn rb_get_cfp_pc(cfp: *mut rb_control_frame_struct) -> *mut VALUE; - pub fn rb_get_cfp_sp(cfp: *mut rb_control_frame_struct) -> *mut VALUE; - pub fn rb_get_cfp_self(cfp: *mut rb_control_frame_struct) -> VALUE; - pub fn rb_get_cfp_ep(cfp: *mut rb_control_frame_struct) -> *mut VALUE; - pub fn rb_get_cfp_ep_level(cfp: *mut rb_control_frame_struct, lv: u32) -> *const VALUE; - pub fn rb_vm_base_ptr(cfp: *mut rb_control_frame_struct) -> *mut VALUE; + pub fn rb_vm_ci_argc(ci: *const rb_callinfo) -> ::std::os::raw::c_uint; + pub fn rb_vm_ci_mid(ci: *const rb_callinfo) -> ID; + pub fn rb_vm_ci_flag(ci: *const rb_callinfo) -> ::std::os::raw::c_uint; + pub fn rb_vm_ci_kwarg(ci: *const rb_callinfo) -> *const rb_callinfo_kwarg; + pub fn rb_get_cikw_keyword_len(cikw: *const rb_callinfo_kwarg) -> ::std::os::raw::c_int; + pub fn rb_get_cikw_keywords_idx( + cikw: *const rb_callinfo_kwarg, + idx: ::std::os::raw::c_int, + ) -> VALUE; + pub fn rb_METHOD_ENTRY_VISI(me: *const rb_callable_method_entry_t) -> rb_method_visibility_t; pub fn rb_get_cme_def_type(cme: *const rb_callable_method_entry_t) -> rb_method_type_t; pub fn rb_get_cme_def_body_attr_id(cme: *const rb_callable_method_entry_t) -> ID; pub fn rb_get_cme_def_body_optimized_type( @@ -961,6 +986,7 @@ unsafe extern "C" { pub fn rb_get_mct_func(mct: *const rb_method_cfunc_t) -> *mut ::std::os::raw::c_void; pub fn rb_get_def_iseq_ptr(def: *mut rb_method_definition_t) -> *const rb_iseq_t; pub fn rb_get_iseq_body_local_iseq(iseq: *const rb_iseq_t) -> *const rb_iseq_t; + pub fn rb_get_iseq_body_local_table_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_get_iseq_body_iseq_encoded(iseq: *const rb_iseq_t) -> *mut VALUE; pub fn rb_get_iseq_body_stack_max(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; pub fn rb_get_iseq_body_type(iseq: *const rb_iseq_t) -> rb_iseq_type; @@ -983,48 +1009,22 @@ unsafe extern "C" { pub fn rb_get_iseq_body_param_lead_num(iseq: *const rb_iseq_t) -> ::std::os::raw::c_int; pub fn rb_get_iseq_body_param_opt_num(iseq: *const rb_iseq_t) -> ::std::os::raw::c_int; pub fn rb_get_iseq_body_param_opt_table(iseq: *const rb_iseq_t) -> *const VALUE; - pub fn rb_get_iseq_body_local_table_size(iseq: *const rb_iseq_t) -> ::std::os::raw::c_uint; - pub fn rb_get_cikw_keyword_len(cikw: *const rb_callinfo_kwarg) -> ::std::os::raw::c_int; - pub fn rb_get_cikw_keywords_idx( - cikw: *const rb_callinfo_kwarg, - idx: ::std::os::raw::c_int, - ) -> VALUE; - pub fn rb_get_call_data_ci(cd: *const rb_call_data) -> *const rb_callinfo; + pub fn rb_get_ec_cfp(ec: *const rb_execution_context_t) -> *mut rb_control_frame_struct; + pub fn rb_get_cfp_iseq(cfp: *mut rb_control_frame_struct) -> *const rb_iseq_t; + pub fn rb_get_cfp_pc(cfp: *mut rb_control_frame_struct) -> *mut VALUE; + pub fn rb_get_cfp_sp(cfp: *mut rb_control_frame_struct) -> *mut VALUE; + pub fn rb_get_cfp_self(cfp: *mut rb_control_frame_struct) -> VALUE; + pub fn rb_get_cfp_ep(cfp: *mut rb_control_frame_struct) -> *mut VALUE; + pub fn rb_get_cfp_ep_level(cfp: *mut rb_control_frame_struct, lv: u32) -> *const VALUE; + pub fn rb_yarv_class_of(obj: VALUE) -> VALUE; pub fn rb_FL_TEST(obj: VALUE, flags: VALUE) -> VALUE; pub fn rb_FL_TEST_RAW(obj: VALUE, flags: VALUE) -> VALUE; pub fn rb_RB_TYPE_P(obj: VALUE, t: ruby_value_type) -> bool; pub fn rb_RSTRUCT_LEN(st: VALUE) -> ::std::os::raw::c_long; + pub fn rb_get_call_data_ci(cd: *const rb_call_data) -> *const rb_callinfo; pub fn rb_BASIC_OP_UNREDEFINED_P(bop: ruby_basic_operators, klass: u32) -> bool; - pub fn rb_zjit_multi_ractor_p() -> bool; + pub fn rb_RCLASS_ORIGIN(c: VALUE) -> VALUE; pub fn rb_assert_iseq_handle(handle: VALUE); - pub fn rb_zjit_constcache_shareable(ice: *const iseq_inline_constant_cache_entry) -> bool; - pub fn rb_assert_cme_handle(handle: VALUE); pub fn rb_IMEMO_TYPE_P(imemo: VALUE, imemo_type: imemo_type) -> ::std::os::raw::c_int; - pub fn rb_zjit_vm_unlock( - recursive_lock_level: *mut ::std::os::raw::c_uint, - file: *const ::std::os::raw::c_char, - line: ::std::os::raw::c_int, - ); - pub fn rb_zjit_mark_writable(mem_block: *mut ::std::os::raw::c_void, mem_size: u32) -> bool; - pub fn rb_zjit_mark_executable(mem_block: *mut ::std::os::raw::c_void, mem_size: u32); - pub fn rb_zjit_mark_unused(mem_block: *mut ::std::os::raw::c_void, mem_size: u32) -> bool; - pub fn rb_zjit_icache_invalidate( - start: *mut ::std::os::raw::c_void, - end: *mut ::std::os::raw::c_void, - ); - pub fn rb_vm_ci_argc(ci: *const rb_callinfo) -> ::std::os::raw::c_uint; - pub fn rb_vm_ci_mid(ci: *const rb_callinfo) -> ID; - pub fn rb_vm_ci_flag(ci: *const rb_callinfo) -> ::std::os::raw::c_uint; - pub fn rb_vm_ci_kwarg(ci: *const rb_callinfo) -> *const rb_callinfo_kwarg; - pub fn rb_METHOD_ENTRY_VISI(me: *const rb_callable_method_entry_t) -> rb_method_visibility_t; - pub fn rb_yarv_class_of(obj: VALUE) -> VALUE; - pub fn rb_zjit_vm_lock_then_barrier( - recursive_lock_level: *mut ::std::os::raw::c_uint, - file: *const ::std::os::raw::c_char, - line: ::std::os::raw::c_int, - ); - pub fn rb_RCLASS_ORIGIN(c: VALUE) -> VALUE; - pub fn rb_iseq_get_zjit_payload(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_void; - pub fn rb_iseq_set_zjit_payload(iseq: *const rb_iseq_t, payload: *mut ::std::os::raw::c_void); - pub fn rb_zjit_print_exception(); + pub fn rb_assert_cme_handle(handle: VALUE); } diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index d46f5f486f..da56738231 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -141,6 +141,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> { write!(f, "BOPRedefined(")?; match klass { INTEGER_REDEFINED_OP_FLAG => write!(f, "INTEGER_REDEFINED_OP_FLAG")?, + ARRAY_REDEFINED_OP_FLAG => write!(f, "ARRAY_REDEFINED_OP_FLAG")?, _ => write!(f, "{klass}")?, } write!(f, ", ")?; @@ -156,6 +157,7 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> { BOP_LE => write!(f, "BOP_LE")?, BOP_GT => write!(f, "BOP_GT")?, BOP_GE => write!(f, "BOP_GE")?, + BOP_MAX => write!(f, "BOP_MAX")?, _ => write!(f, "{bop}")?, } write!(f, ")") @@ -310,6 +312,7 @@ pub enum Insn { NewArray { elements: Vec<InsnId>, state: InsnId }, ArraySet { array: InsnId, idx: usize, val: InsnId }, ArrayDup { val: InsnId, state: InsnId }, + ArrayMax { elements: Vec<InsnId>, state: InsnId }, // Check if the value is truthy and "return" a C boolean. In reality, we will likely fuse this // with IfTrue/IfFalse in the backend to generate jcc. @@ -441,6 +444,15 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) } + Insn::ArrayMax { elements, .. } => { + write!(f, "ArrayMax")?; + let mut prefix = " "; + for element in elements { + write!(f, "{prefix}{element}")?; + prefix = ", "; + } + Ok(()) + } Insn::ArraySet { array, idx, val } => { write!(f, "ArraySet {array}, {idx}, {val}") } Insn::ArrayDup { val, .. } => { write!(f, "ArrayDup {val}") } Insn::StringCopy { val } => { write!(f, "StringCopy {val}") } @@ -619,7 +631,7 @@ impl<T: Copy + Into<usize> + PartialEq> UnionFind<T> { } /// Find the set representative for `insn` without doing path compression. - pub fn find_const(&self, insn: T) -> T { + fn find_const(&self, insn: T) -> T { let mut result = insn; loop { match self.at(result) { @@ -645,7 +657,7 @@ pub struct Function { // TODO: get method name and source location from the ISEQ insns: Vec<Insn>, - union_find: UnionFind<InsnId>, + union_find: std::cell::RefCell<UnionFind<InsnId>>, insn_types: Vec<Type>, blocks: Vec<Block>, entry_block: BlockId, @@ -657,7 +669,7 @@ impl Function { iseq, insns: vec![], insn_types: vec![], - union_find: UnionFind::new(), + union_find: UnionFind::new().into(), blocks: vec![Block::default()], entry_block: BlockId(0), } @@ -740,7 +752,14 @@ impl Function { macro_rules! find { ( $x:expr ) => { { - self.union_find.find_const($x) + self.union_find.borrow_mut().find($x) + } + }; + } + macro_rules! find_vec { + ( $x:expr ) => { + { + $x.iter().map(|arg| find!(*arg)).collect() } }; } @@ -749,15 +768,15 @@ impl Function { { BranchEdge { target: $edge.target, - args: $edge.args.iter().map(|x| self.union_find.find_const(*x)).collect(), + args: find_vec!($edge.args), } } }; } - let insn_id = self.union_find.find_const(insn_id); + let insn_id = self.union_find.borrow_mut().find(insn_id); use Insn::*; match &self.insns[insn_id.0] { - result@(PutSelf | Const {..} | Param {..} | NewArray {..} | GetConstantPath {..} + result@(PutSelf | Const {..} | Param {..} | GetConstantPath {..} | PatchPoint {..}) => result.clone(), Snapshot { state: FrameState { iseq, insn_idx, pc, stack, locals } } => Snapshot { @@ -816,18 +835,20 @@ impl Function { ArrayDup { val , state } => ArrayDup { val: find!(*val), state: *state }, CCall { cfun, args, name, return_type } => CCall { cfun: *cfun, args: args.iter().map(|arg| find!(*arg)).collect(), name: *name, return_type: *return_type }, Defined { .. } => todo!("find(Defined)"), + NewArray { elements, state } => NewArray { elements: find_vec!(*elements), state: find!(*state) }, + ArrayMax { elements, state } => ArrayMax { elements: find_vec!(*elements), state: find!(*state) }, } } /// Replace `insn` with the new instruction `replacement`, which will get appended to `insns`. fn make_equal_to(&mut self, insn: InsnId, replacement: InsnId) { // Don't push it to the block - self.union_find.make_equal_to(insn, replacement); + self.union_find.borrow_mut().make_equal_to(insn, replacement); } fn type_of(&self, insn: InsnId) -> Type { assert!(self.insns[insn.0].has_output()); - self.insn_types[self.union_find.find_const(insn).0] + self.insn_types[self.union_find.borrow_mut().find(insn).0] } /// Check if the type of `insn` is a subtype of `ty`. @@ -882,6 +903,7 @@ impl Function { Insn::PutSelf => types::BasicObject, Insn::Defined { .. } => types::BasicObject, Insn::GetConstantPath { .. } => types::BasicObject, + Insn::ArrayMax { .. } => types::BasicObject, } } @@ -1309,9 +1331,13 @@ impl Function { necessary[insn_id.0] = true; match self.find(insn_id) { Insn::PutSelf | Insn::Const { .. } | Insn::Param { .. } - | Insn::NewArray { .. } | Insn::PatchPoint(..) - | Insn::GetConstantPath { .. } => + | Insn::PatchPoint(..) | Insn::GetConstantPath { .. } => {} + Insn::ArrayMax { elements, state } + | Insn::NewArray { elements, state } => { + worklist.extend(elements); + worklist.push_back(state); + } Insn::StringCopy { val } | Insn::StringIntern { val } | Insn::Return { val } @@ -1600,6 +1626,10 @@ fn compute_jump_targets(iseq: *const rb_iseq_t) -> Vec<u32> { let offset = get_arg(pc, 0).as_i64(); jump_targets.insert(insn_idx_at_offset(insn_idx, offset)); } + YARVINSN_opt_new => { + let offset = get_arg(pc, 1).as_i64(); + jump_targets.insert(insn_idx_at_offset(insn_idx, offset)); + } YARVINSN_leave | YARVINSN_opt_invokebuiltin_delegate_leave => { if insn_idx < iseq_size { jump_targets.insert(insn_idx); @@ -1632,6 +1662,7 @@ pub enum CallType { pub enum ParseError { StackUnderflow(FrameState), UnknownOpcode(String), + UnknownNewArraySend(String), UnhandledCallType(CallType), } @@ -1751,6 +1782,26 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { elements.reverse(); state.stack_push(fun.push_insn(block, Insn::NewArray { elements, state: exit_id })); } + YARVINSN_opt_newarray_send => { + let count = get_arg(pc, 0).as_usize(); + let method = get_arg(pc, 1).as_u32(); + let mut elements = vec![]; + for _ in 0..count { + elements.push(state.stack_pop()?); + } + elements.reverse(); + let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.clone() }); + let (bop, insn) = match method { + VM_OPT_NEWARRAY_SEND_MAX => (BOP_MAX, Insn::ArrayMax { elements, state: exit_id }), + VM_OPT_NEWARRAY_SEND_MIN => return Err(ParseError::UnknownNewArraySend("min".into())), + VM_OPT_NEWARRAY_SEND_HASH => return Err(ParseError::UnknownNewArraySend("hash".into())), + VM_OPT_NEWARRAY_SEND_PACK => return Err(ParseError::UnknownNewArraySend("pack".into())), + VM_OPT_NEWARRAY_SEND_PACK_BUFFER => return Err(ParseError::UnknownNewArraySend("pack_buffer".into())), + _ => return Err(ParseError::UnknownNewArraySend(format!("{method}"))), + }; + fun.push_insn(block, Insn::PatchPoint(Invariant::BOPRedefined { klass: ARRAY_REDEFINED_OP_FLAG, bop })); + state.stack_push(fun.push_insn(block, insn)); + } YARVINSN_duparray => { let val = fun.push_insn(block, Insn::Const { val: Const::Value(get_arg(pc, 0)) }); let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.clone() }); @@ -1800,6 +1851,17 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { }); queue.push_back((state.clone(), target, target_idx)); } + YARVINSN_opt_new => { + let offset = get_arg(pc, 1).as_i64(); + // TODO(max): Check interrupts + let target_idx = insn_idx_at_offset(insn_idx, offset); + let target = insn_idx_to_block[&target_idx]; + // Skip the fast-path and go straight to the fallback code. We will let the + // optimizer take care of the converting Class#new->alloc+initialize instead. + fun.push_insn(block, Insn::Jump(BranchEdge { target, args: state.as_args() })); + queue.push_back((state.clone(), target, target_idx)); + break; // Don't enqueue the next block as a successor + } YARVINSN_jump => { let offset = get_arg(pc, 0).as_i64(); // TODO(max): Check interrupts @@ -1963,19 +2025,19 @@ mod union_find_tests { } #[test] - fn test_find_const_returns_target() { + fn test_find_returns_target() { let mut uf = UnionFind::new(); uf.make_equal_to(3, 4); - assert_eq!(uf.find_const(3usize), 4); + assert_eq!(uf.find(3usize), 4); } #[test] - fn test_find_const_returns_transitive_target() { + fn test_find_returns_transitive_target() { let mut uf = UnionFind::new(); uf.make_equal_to(3, 4); uf.make_equal_to(4, 5); - assert_eq!(uf.find_const(3usize), 5); - assert_eq!(uf.find_const(4usize), 5); + assert_eq!(uf.find(3usize), 5); + assert_eq!(uf.find(4usize), 5); } #[test] @@ -2796,6 +2858,55 @@ mod tests { "); assert_compile_fails("test", ParseError::UnknownOpcode("sendforward".into())) } + + #[test] + fn test_opt_new() { + eval(" + class C; end + def test = C.new + "); + assert_method_hir("test", expect![[r#" + fn test: + bb0(): + v1:BasicObject = GetConstantPath 0x1000 + v2:NilClassExact = Const Value(nil) + Jump bb1(v2, v1) + bb1(v4:NilClassExact, v5:BasicObject): + v8:BasicObject = SendWithoutBlock v5, :new + Jump bb2(v8, v4) + bb2(v10:BasicObject, v11:NilClassExact): + Return v10 + "#]]); + } + + #[test] + fn test_opt_newarray_send_max_no_elements() { + eval(" + def test = [].max + "); + // TODO(max): Rewrite to nil + assert_method_hir("test", expect![[r#" + fn test: + bb0(): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX) + v3:BasicObject = ArrayMax + Return v3 + "#]]); + } + + #[test] + fn test_opt_newarray_send_max() { + eval(" + def test(a,b) = [a,b].max + "); + assert_method_hir("test", expect![[r#" + fn test: + bb0(v0:BasicObject, v1:BasicObject): + PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX) + v5:BasicObject = ArrayMax v0, v1 + Return v5 + "#]]); + } } #[cfg(test)] @@ -3688,4 +3799,55 @@ mod opt_tests { Return v5 "#]]); } + + #[test] + fn test_opt_new_no_initialize() { + eval(" + class C; end + def test = C.new + test + "); + assert_optimized_method_hir("test", expect![[r#" + fn test: + bb0(): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, C) + v16:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v2:NilClassExact = Const Value(nil) + Jump bb1(v2, v16) + bb1(v4:NilClassExact, v5:BasicObject[VALUE(0x1008)]): + v8:BasicObject = SendWithoutBlock v5, :new + Jump bb2(v8, v4) + bb2(v10:BasicObject, v11:NilClassExact): + Return v10 + "#]]); + } + + #[test] + fn test_opt_new_initialize() { + eval(" + class C + def initialize x + @x = x + end + end + def test = C.new 1 + test + "); + assert_optimized_method_hir("test", expect![[r#" + fn test: + bb0(): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1000, C) + v18:BasicObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v2:NilClassExact = Const Value(nil) + v3:Fixnum[1] = Const Value(1) + Jump bb1(v2, v18, v3) + bb1(v5:NilClassExact, v6:BasicObject[VALUE(0x1008)], v7:Fixnum[1]): + v10:BasicObject = SendWithoutBlock v6, :new, v7 + Jump bb2(v10, v5) + bb2(v12:BasicObject, v13:NilClassExact): + Return v12 + "#]]); + } } diff --git a/zjit/zjit.mk b/zjit/zjit.mk index d24d1a19c4..91cf861a39 100644 --- a/zjit/zjit.mk +++ b/zjit/zjit.mk @@ -99,7 +99,7 @@ ZJIT_BINDGEN_DIFF_OPTS = # Generate Rust bindings. See source for details. # Needs `./configure --enable-zjit=dev` and Clang. ifneq ($(strip $(CARGO)),) # if configure found Cargo -.PHONY: zjit-bindgen zjit-bindgen-show-unused +.PHONY: zjit-bindgen zjit-bindgen-show-unused zjit-test zjit-test-lldb zjit-bindgen: zjit.$(OBJEXT) ZJIT_SRC_ROOT_PATH='$(top_srcdir)' BINDGEN_JIT_NAME=zjit $(CARGO) run --manifest-path '$(top_srcdir)/zjit/bindgen/Cargo.toml' -- $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(Q) if [ 'x$(HAVE_GIT)' = xyes ]; then $(GIT) -C "$(top_srcdir)" diff $(ZJIT_BINDGEN_DIFF_OPTS) zjit/src/cruby_bindings.inc.rs; fi @@ -116,6 +116,21 @@ zjit-test: libminiruby.a CARGO_TARGET_DIR='$(ZJIT_CARGO_TARGET_DIR)' \ $(CARGO) nextest run --manifest-path '$(top_srcdir)/zjit/Cargo.toml' $(ZJIT_TESTS) +# Run a ZJIT test written with Rust #[test] under LLDB +zjit-test-lldb: libminiruby.a + $(Q)set -eu; \ + if [ -z '$(ZJIT_TESTS)' ]; then \ + echo "Please pass a ZJIT_TESTS=... filter to make."; \ + echo "Many tests only work when it's the only test in the process."; \ + exit 1; \ + fi; \ + exe_path=`RUBY_BUILD_DIR='$(TOP_BUILD_DIR)' \ + RUBY_LD_FLAGS='$(LDFLAGS) $(XLDFLAGS) $(MAINLIBS)' \ + CARGO_TARGET_DIR='$(ZJIT_CARGO_TARGET_DIR)' \ + $(CARGO) nextest list --manifest-path '$(top_srcdir)/zjit/Cargo.toml' --message-format json --list-type=binaries-only | \ + $(BASERUBY) -rjson -e 'puts JSON.load(STDIN.read).dig("rust-binaries", "zjit", "binary-path")'`; \ + exec lldb $$exe_path -- --test-threads=1 $(ZJIT_TESTS) + # A library for booting miniruby in tests. # Why not use libruby-static.a for this? # - Initialization of the full ruby involves dynamic linking for e.g. transcoding implementations |