diff --git a/.document b/.document index ec2fa093261427..7c6d9967b1bbdf 100644 --- a/.document +++ b/.document @@ -23,6 +23,7 @@ nilclass.rb pack.rb ractor.rb string.rb +symbol.rb timev.rb thread_sync.rb trace_point.rb diff --git a/.github/workflows/mjit-bindgen.yml b/.github/workflows/mjit-bindgen.yml index 1252d3b50987c6..7c1575634d8e74 100644 --- a/.github/workflows/mjit-bindgen.yml +++ b/.github/workflows/mjit-bindgen.yml @@ -28,8 +28,6 @@ jobs: include: - task: mjit-bindgen fail-fast: false - env: - SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }} runs-on: ubuntu-20.04 if: ${{ !contains(github.event.head_commit.message, '[DOC]') && !contains(github.event.pull_request.labels.*.name, 'Documentation') }} steps: @@ -39,20 +37,20 @@ jobs: run: | echo "GNUMAKEFLAGS=-j$((1 + $(nproc --all)))" >> $GITHUB_ENV - name: Install libraries - env: - arch: ${{ matrix.arch }} run: | set -x - arch=${arch:+:${arch/i[3-6]86/i386}} - ${arch:+sudo dpkg --add-architecture ${arch#:}} sudo apt-get update -q || : sudo apt-get install --no-install-recommends -q -y \ - ${arch:+cross}build-essential${arch/:/-} \ - libssl-dev${arch} libyaml-dev${arch} libreadline6-dev${arch} \ - zlib1g-dev${arch} libncurses5-dev${arch} libffi-dev${arch} \ - libclang1-10${arch} \ - bison autoconf ruby - sudo apt-get install -q -y pkg-config${arch} || : + build-essential \ + libssl-dev libyaml-dev libreadline6-dev \ + zlib1g-dev libncurses5-dev libffi-dev \ + libclang1-10 \ + bison autoconf + sudo apt-get install -q -y pkg-config || : + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.1' - name: git config run: | git config --global advice.detachedHead 0 @@ -72,15 +70,11 @@ jobs: - run: ./autogen.sh working-directory: src - name: Run configure - env: - arch: ${{ matrix.arch }} - run: >- - $SETARCH ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug - ${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE} - - run: $SETARCH make incs - - run: $SETARCH make - - run: $SETARCH make install - - run: $SETARCH make ${{ matrix.task }} + run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug + - run: make incs + - run: make + - run: make install + - run: make ${{ matrix.task }} - run: git diff --exit-code working-directory: src - uses: ruby/action-slack@v3.0.0 diff --git a/NEWS.md b/NEWS.md index b4ecccc5c4e39b..bff9d1e7ab51b4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -103,7 +103,8 @@ Note that each entry is kept to a minimum, see links for details. Note: We're only listing outstanding class updates. * Fiber::Scheduler - * Introduce `Fiber::Scheduler#io_select` for non-blocking `IO.select`. [[Feature #19060]] + * Introduce `Fiber::Scheduler#io_select` for non-blocking `IO.select`. + [[Feature #19060]] * IO * Introduce `IO#timeout=` and `IO#timeout` which can cause @@ -115,6 +116,11 @@ Note: We're only listing outstanding class updates. STDIN.read # => Blocking operation timed out! (IO::TimeoutError) ``` +* UNIXSocket + * Add support for `UNIXSocket` on Windows. Emulate anonymous sockets. Add + support for `File.socket?` and `File::Stat#socket?` where possible. + [[Feature #19135]] + * Class * `Class#attached_object`, which returns the object for which the receiver is the singleton class. Raises `TypeError` if the @@ -239,7 +245,7 @@ Note: We're only listing outstanding class updates. * io-nonblock 0.1.1 * io-wait 0.3.0.pre * ipaddr 1.2.4 - * irb 1.4.2 + * irb 1.4.3 * json 2.6.2 * logger 1.5.1 * net-http 0.3.0 @@ -301,7 +307,22 @@ The following deprecated methods are removed. * `Psych` no longer bundles libyaml sources. And also `Fiddle` no longer bundles libffi sources. Users need to install the libyaml/libffi library themselves via the package - system. [[Feature #18571]] + manager like apt, yum, brew, etc. + + Psych and fiddle supported the static build with specific version of libyaml + and libffi sources. You can build psych with libyaml-0.2.5 like this. + + ```bash + $ ./configure --with-libyaml-source-dir=/path/to/libyaml-0.2.5 + ``` + + And you can build fiddle with libffi-3.4.4 like this. + + ```bash + $ ./configure --with-libffi-source-dir=/path/to/libffi-3.4.4 + ``` + + [[Feature #18571]] ## C API updates @@ -356,6 +377,7 @@ The following deprecated APIs are removed. doing it in a native thread called MJIT worker. [[Feature #18968]] * As a result, Microsoft Visual Studio (MSWIN) is no longer supported. * MinGW is no longer supported. [[Feature #18824]] +* Rename `--mjit-min-calls` to `--mjit-call-threshold`. ## Static analysis @@ -416,3 +438,4 @@ The following deprecated APIs are removed. [Feature #19026]: https://bugs.ruby-lang.org/issues/19026 [Feature #19060]: https://bugs.ruby-lang.org/issues/19060 [Bug #19100]: https://bugs.ruby-lang.org/issues/19100 +[Feature #19135]: https://bugs.ruby-lang.org/issues/19135 diff --git a/README.md b/README.md index 35ef2d9e8989bc..5b1f21abc90298 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,50 @@ see [Building Ruby](doc/contributing/building_ruby.md) https://www.ruby-lang.org/ +## Commands + +The following commands are available on IRB. + +* `cwws` + * Show the current workspace. +* `cb`, `cws`, `chws` + * Change the current workspace to an object. +* `bindings`, `workspaces` + * Show workspaces. +* `pushb`, `pushws` + * Push an object to the workspace stack. +* `popb`, `popws` + * Pop a workspace from the workspace stack. +* `load` + * Load a Ruby file. +* `require` + * Require a Ruby file. +* `source` + * Loads a given file in the current session. +* `irb` + * Start a child IRB. +* `jobs` + * List of current sessions. +* `fg` + * Switches to the session of the given number. +* `kill` + * Kills the session with the given number. +* `help` + * Enter the mode to look up RI documents. +* `irb_info` + * Show information about IRB. +* `ls` + * Show methods, constants, and variables. + `-g [query]` or `-G [query]` allows you to filter out the output. +* `measure` + * `measure` enables the mode to measure processing time. `measure :off` disables it. +* `$`, `show_source` + * Show the source code of a given method or constant. +* `@`, `whereami` + * Show the source code around binding.irb again. +* `debug` + * Start the debugger of debug.gem. + ## Documentation - [English](https://docs.ruby-lang.org/en/master/index.html) diff --git a/array.c b/array.c index 0b4998a47c0d1d..f242031b4fffb7 100644 --- a/array.c +++ b/array.c @@ -4555,7 +4555,7 @@ take_items(VALUE obj, long n) if (!NIL_P(result)) return rb_ary_subseq(result, 0, n); result = rb_ary_new2(n); args[0] = result; args[1] = (VALUE)n; - if (rb_check_block_call(obj, idEach, 0, 0, take_i, (VALUE)args) == Qundef) + if (UNDEF_P(rb_check_block_call(obj, idEach, 0, 0, take_i, (VALUE)args))) rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)", rb_obj_class(obj)); return result; @@ -5048,7 +5048,7 @@ rb_ary_fill(int argc, VALUE *argv, VALUE ary) ARY_SET_LEN(ary, end); } - if (item == Qundef) { + if (UNDEF_P(item)) { VALUE v; long i; @@ -5505,7 +5505,7 @@ rb_ary_cmp(VALUE ary1, VALUE ary2) if (NIL_P(ary2)) return Qnil; if (ary1 == ary2) return INT2FIX(0); v = rb_exec_recursive_paired(recursive_cmp, ary1, ary2, ary2); - if (v != Qundef) return v; + if (!UNDEF_P(v)) return v; len = RARRAY_LEN(ary1) - RARRAY_LEN(ary2); if (len == 0) return INT2FIX(0); if (len > 0) return INT2FIX(1); @@ -6068,7 +6068,7 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary) if (rb_block_given_p()) { for (i = 0; i < RARRAY_LEN(ary); i++) { v = RARRAY_AREF(ary, i); - if (result == Qundef || rb_cmpint(rb_yield_values(2, v, result), v, result) > 0) { + if (UNDEF_P(result) || rb_cmpint(rb_yield_values(2, v, result), v, result) > 0) { result = v; } } @@ -6090,7 +6090,7 @@ rb_ary_max(int argc, VALUE *argv, VALUE ary) } } } - if (result == Qundef) return Qnil; + if (UNDEF_P(result)) return Qnil; return result; } @@ -6237,7 +6237,7 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary) if (rb_block_given_p()) { for (i = 0; i < RARRAY_LEN(ary); i++) { v = RARRAY_AREF(ary, i); - if (result == Qundef || rb_cmpint(rb_yield_values(2, v, result), v, result) < 0) { + if (UNDEF_P(result) || rb_cmpint(rb_yield_values(2, v, result), v, result) < 0) { result = v; } } @@ -6259,7 +6259,7 @@ rb_ary_min(int argc, VALUE *argv, VALUE ary) } } } - if (result == Qundef) return Qnil; + if (UNDEF_P(result)) return Qnil; return result; } @@ -8148,7 +8148,7 @@ finish_exact_sum(long n, VALUE r, VALUE v, int z) { if (n != 0) v = rb_fix_plus(LONG2FIX(n), v); - if (r != Qundef) { + if (!UNDEF_P(r)) { v = rb_rational_plus(r, v); } else if (!n && z) { @@ -8227,7 +8227,7 @@ rb_ary_sum(int argc, VALUE *argv, VALUE ary) else if (RB_BIGNUM_TYPE_P(e)) v = rb_big_plus(e, v); else if (RB_TYPE_P(e, T_RATIONAL)) { - if (r == Qundef) + if (UNDEF_P(r)) r = e; else r = rb_rational_plus(r, e); diff --git a/benchmark/lib/benchmark_driver/runner/mjit.rb b/benchmark/lib/benchmark_driver/runner/mjit.rb index 1d4693e8be2a81..3a58a620dec5a4 100644 --- a/benchmark/lib/benchmark_driver/runner/mjit.rb +++ b/benchmark/lib/benchmark_driver/runner/mjit.rb @@ -16,7 +16,7 @@ def parse(**) job.prelude = "#{job.prelude}\n#{<<~EOS}" if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? __bmdv_ruby_i = 0 - while __bmdv_ruby_i < 10000 # jit_min_calls + while __bmdv_ruby_i < 10000 # MJIT call threshold #{job.script} __bmdv_ruby_i += 1 end diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 491c700a1c1c7c..16082ccd51f837 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -285,7 +285,7 @@ def test n 30.times.map{|i| test i } -} unless ENV['RUN_OPTS'] =~ /--jit-min-calls=5/ || # This always fails with --jit-wait --jit-min-calls=5 +} unless ENV['RUN_OPTS'] =~ /--mjit-call-threshold=5/ || # This always fails with --mjit-wait --mjit-call-threshold=5 (ENV.key?('TRAVIS') && ENV['TRAVIS_CPU_ARCH'] == 'arm64') # https://bugs.ruby-lang.org/issues/17878 # Exception for empty select diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 46fb5838dc60f2..9bcead3065ac57 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -47,6 +47,29 @@ def call_graph_root Object.send(:remove_const, :Foo) } +assert_normal_exit %q{ + # Test to ensure send on overriden c functions + # doesn't corrupt the stack + class Bar + def bar(x) + x + end + end + + class Foo + def bar + Bar.new + end + end + + foo = Foo.new + # before this change, this line would error + # because "s" would still be on the stack + # String.to_s is the overridden method here + p foo.bar.bar("s".__send__(:to_s)) +} + + assert_equal '[nil, nil, nil, nil, nil, nil]', %q{ [NilClass, TrueClass, FalseClass, Integer, Float, Symbol].each do |klass| klass.class_eval("def foo = @foo") diff --git a/class.c b/class.c index 85663ada50ab79..b9d62c18b19e83 100644 --- a/class.c +++ b/class.c @@ -64,7 +64,7 @@ push_subclass_entry_to_list(VALUE super, VALUE klass) void rb_class_subclass_add(VALUE super, VALUE klass) { - if (super && super != Qundef) { + if (super && !UNDEF_P(super)) { rb_subclass_entry_t *entry = push_subclass_entry_to_list(super, klass); RCLASS_SUBCLASS_ENTRY(klass) = entry; } @@ -277,7 +277,7 @@ rb_class_update_superclasses(VALUE klass) VALUE super = RCLASS_SUPER(klass); if (!RB_TYPE_P(klass, T_CLASS)) return; - if (super == Qundef) return; + if (UNDEF_P(super)) return; // If the superclass array is already built if (RCLASS_SUPERCLASSES(klass)) @@ -608,7 +608,7 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach) arg.klass = clone; rb_id_table_foreach(RCLASS_CONST_TBL(klass), clone_const_i, &arg); } - if (attach != Qundef) { + if (!UNDEF_P(attach)) { rb_singleton_class_attached(clone, attach); } RCLASS_M_TBL_INIT(clone); diff --git a/common.mk b/common.mk index ef4b952eda8937..24c1b39c4a2a34 100644 --- a/common.mk +++ b/common.mk @@ -249,19 +249,7 @@ mjit_config.h: Makefile .PHONY: mjit-bindgen mjit-bindgen: - $(Q)$(XRUBY) -C $(srcdir) -Ilib \ - -e 'ENV["GEM_HOME"] = File.expand_path(".bundle")' \ - -e 'ENV["BUNDLE_APP_CONFIG"] = File.expand_path(".bundle")' \ - -e 'ENV["BUNDLE_PATH__SYSTEM"] = "true"' \ - -e 'ENV["BUNDLE_WITHOUT"] = "lint doc"' \ - -e 'load "spec/bundler/support/bundle.rb"' -- install --gemfile=tool/mjit/Gemfile - $(Q)$(XRUBY) -C $(srcdir) -Ilib \ - -e 'ENV["GEM_HOME"] = File.expand_path(".bundle")' \ - -e 'ENV["BUNDLE_APP_CONFIG"] = File.expand_path(".bundle")' \ - -e 'ENV["BUNDLE_GEMFILE"] = "tool/mjit/Gemfile"' \ - -e 'ENV["BUNDLE_PATH__SYSTEM"] = "true"' \ - -e 'ENV["BUNDLE_WITHOUT"] = "lint doc"' \ - -e 'load "spec/bundler/support/bundle.rb"' -- exec tool/mjit/bindgen.rb $(CURDIR) + $(Q) $(BASERUBY) -rrubygems -C $(srcdir)/tool/mjit bindgen.rb $(CURDIR) # These rules using MJIT_HEADER_SUFFIX must be in common.mk, not # Makefile.in, in order to override the macro in defs/universal.mk. @@ -903,7 +891,7 @@ $(RBCONFIG): $(tooldir)/mkconfig.rb config.status $(srcdir)/version.h test-rubyspec: test-spec yes-test-rubyspec: yes-test-spec -test-spec-precheck: programs +test-spec-precheck: programs yes-fake test-spec: $(TEST_RUNNABLE)-test-spec yes-test-spec: test-spec-precheck @@ -1104,6 +1092,7 @@ BUILTIN_RB_SRCS = \ $(srcdir)/array.rb \ $(srcdir)/kernel.rb \ $(srcdir)/ractor.rb \ + $(srcdir)/symbol.rb \ $(srcdir)/timev.rb \ $(srcdir)/thread_sync.rb \ $(srcdir)/nilclass.rb \ @@ -6748,7 +6737,6 @@ gc.$(OBJEXT): {$(VPATH)}encoding.h gc.$(OBJEXT): {$(VPATH)}eval_intern.h gc.$(OBJEXT): {$(VPATH)}gc.c gc.$(OBJEXT): {$(VPATH)}gc.h -gc.$(OBJEXT): {$(VPATH)}gc.rb gc.$(OBJEXT): {$(VPATH)}gc.rbinc gc.$(OBJEXT): {$(VPATH)}id.h gc.$(OBJEXT): {$(VPATH)}id_table.h @@ -6967,7 +6955,6 @@ goruby.$(OBJEXT): {$(VPATH)}config.h goruby.$(OBJEXT): {$(VPATH)}constant.h goruby.$(OBJEXT): {$(VPATH)}defines.h goruby.$(OBJEXT): {$(VPATH)}golf_prelude.c -goruby.$(OBJEXT): {$(VPATH)}golf_prelude.rb goruby.$(OBJEXT): {$(VPATH)}goruby.c goruby.$(OBJEXT): {$(VPATH)}id.h goruby.$(OBJEXT): {$(VPATH)}id_table.h @@ -8354,7 +8341,6 @@ load.$(OBJEXT): {$(VPATH)}vm_core.h load.$(OBJEXT): {$(VPATH)}vm_opts.h loadpath.$(OBJEXT): $(hdrdir)/ruby/ruby.h loadpath.$(OBJEXT): $(hdrdir)/ruby/version.h -loadpath.$(OBJEXT): $(top_srcdir)/revision.h loadpath.$(OBJEXT): $(top_srcdir)/version.h loadpath.$(OBJEXT): {$(VPATH)}assert.h loadpath.$(OBJEXT): {$(VPATH)}backward/2/assume.h @@ -8510,6 +8496,7 @@ loadpath.$(OBJEXT): {$(VPATH)}internal/warning_push.h loadpath.$(OBJEXT): {$(VPATH)}internal/xmalloc.h loadpath.$(OBJEXT): {$(VPATH)}loadpath.c loadpath.$(OBJEXT): {$(VPATH)}missing.h +loadpath.$(OBJEXT): {$(VPATH)}revision.h loadpath.$(OBJEXT): {$(VPATH)}st.h loadpath.$(OBJEXT): {$(VPATH)}subst.h loadpath.$(OBJEXT): {$(VPATH)}verconf.h @@ -9600,6 +9587,7 @@ miniinit.$(OBJEXT): {$(VPATH)}ruby_atomic.h miniinit.$(OBJEXT): {$(VPATH)}shape.h miniinit.$(OBJEXT): {$(VPATH)}st.h miniinit.$(OBJEXT): {$(VPATH)}subst.h +miniinit.$(OBJEXT): {$(VPATH)}symbol.rb miniinit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h miniinit.$(OBJEXT): {$(VPATH)}thread_native.h miniinit.$(OBJEXT): {$(VPATH)}thread_sync.rb @@ -9813,7 +9801,6 @@ mjit.$(OBJEXT): {$(VPATH)}method.h mjit.$(OBJEXT): {$(VPATH)}missing.h mjit.$(OBJEXT): {$(VPATH)}mjit.c mjit.$(OBJEXT): {$(VPATH)}mjit.h -mjit.$(OBJEXT): {$(VPATH)}mjit.rb mjit.$(OBJEXT): {$(VPATH)}mjit.rbinc mjit.$(OBJEXT): {$(VPATH)}mjit_config.h mjit.$(OBJEXT): {$(VPATH)}mjit_unit.h @@ -10029,7 +10016,6 @@ mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_c.rbinc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compile_attr.inc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.c mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.h -mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rb mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_compiler.rbinc mjit_compiler.$(OBJEXT): {$(VPATH)}mjit_unit.h mjit_compiler.$(OBJEXT): {$(VPATH)}node.h @@ -10423,7 +10409,6 @@ numeric.$(OBJEXT): {$(VPATH)}internal/warning_push.h numeric.$(OBJEXT): {$(VPATH)}internal/xmalloc.h numeric.$(OBJEXT): {$(VPATH)}missing.h numeric.$(OBJEXT): {$(VPATH)}numeric.c -numeric.$(OBJEXT): {$(VPATH)}numeric.rb numeric.$(OBJEXT): {$(VPATH)}numeric.rbinc numeric.$(OBJEXT): {$(VPATH)}onigmo.h numeric.$(OBJEXT): {$(VPATH)}oniguruma.h @@ -10621,7 +10606,6 @@ object.$(OBJEXT): {$(VPATH)}internal/value_type.h object.$(OBJEXT): {$(VPATH)}internal/variable.h object.$(OBJEXT): {$(VPATH)}internal/warning_push.h object.$(OBJEXT): {$(VPATH)}internal/xmalloc.h -object.$(OBJEXT): {$(VPATH)}kernel.rb object.$(OBJEXT): {$(VPATH)}kernel.rbinc object.$(OBJEXT): {$(VPATH)}missing.h object.$(OBJEXT): {$(VPATH)}nilclass.rbinc @@ -11666,7 +11650,6 @@ ractor.$(OBJEXT): {$(VPATH)}onigmo.h ractor.$(OBJEXT): {$(VPATH)}oniguruma.h ractor.$(OBJEXT): {$(VPATH)}ractor.c ractor.$(OBJEXT): {$(VPATH)}ractor.h -ractor.$(OBJEXT): {$(VPATH)}ractor.rb ractor.$(OBJEXT): {$(VPATH)}ractor.rbinc ractor.$(OBJEXT): {$(VPATH)}ractor_core.h ractor.$(OBJEXT): {$(VPATH)}ruby_assert.h @@ -15425,6 +15408,7 @@ symbol.$(OBJEXT): {$(VPATH)}backward/2/limits.h symbol.$(OBJEXT): {$(VPATH)}backward/2/long_long.h symbol.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h symbol.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h +symbol.$(OBJEXT): {$(VPATH)}builtin.h symbol.$(OBJEXT): {$(VPATH)}config.h symbol.$(OBJEXT): {$(VPATH)}constant.h symbol.$(OBJEXT): {$(VPATH)}debug_counter.h @@ -15597,6 +15581,8 @@ symbol.$(OBJEXT): {$(VPATH)}st.h symbol.$(OBJEXT): {$(VPATH)}subst.h symbol.$(OBJEXT): {$(VPATH)}symbol.c symbol.$(OBJEXT): {$(VPATH)}symbol.h +symbol.$(OBJEXT): {$(VPATH)}symbol.rb +symbol.$(OBJEXT): {$(VPATH)}symbol.rbinc symbol.$(OBJEXT): {$(VPATH)}vm_debug.h symbol.$(OBJEXT): {$(VPATH)}vm_sync.h thread.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h @@ -15822,7 +15808,6 @@ thread.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).c thread.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h thread.$(OBJEXT): {$(VPATH)}thread_native.h thread.$(OBJEXT): {$(VPATH)}thread_sync.c -thread.$(OBJEXT): {$(VPATH)}thread_sync.rb thread.$(OBJEXT): {$(VPATH)}thread_sync.rbinc thread.$(OBJEXT): {$(VPATH)}timev.h thread.$(OBJEXT): {$(VPATH)}vm_core.h @@ -16788,7 +16773,6 @@ version.$(OBJEXT): $(top_srcdir)/internal/static_assert.h version.$(OBJEXT): $(top_srcdir)/internal/variable.h version.$(OBJEXT): $(top_srcdir)/internal/vm.h version.$(OBJEXT): $(top_srcdir)/internal/warnings.h -version.$(OBJEXT): $(top_srcdir)/revision.h version.$(OBJEXT): $(top_srcdir)/version.h version.$(OBJEXT): {$(VPATH)}assert.h version.$(OBJEXT): {$(VPATH)}atomic.h @@ -16953,6 +16937,7 @@ version.$(OBJEXT): {$(VPATH)}method.h version.$(OBJEXT): {$(VPATH)}missing.h version.$(OBJEXT): {$(VPATH)}mjit.h version.$(OBJEXT): {$(VPATH)}node.h +version.$(OBJEXT): {$(VPATH)}revision.h version.$(OBJEXT): {$(VPATH)}ruby_assert.h version.$(OBJEXT): {$(VPATH)}ruby_atomic.h version.$(OBJEXT): {$(VPATH)}shape.h @@ -18229,6 +18214,5 @@ yjit.$(OBJEXT): {$(VPATH)}vm_opts.h yjit.$(OBJEXT): {$(VPATH)}vm_sync.h yjit.$(OBJEXT): {$(VPATH)}yjit.c yjit.$(OBJEXT): {$(VPATH)}yjit.h -yjit.$(OBJEXT): {$(VPATH)}yjit.rb yjit.$(OBJEXT): {$(VPATH)}yjit.rbinc # AUTOGENERATED DEPENDENCIES END diff --git a/compar.c b/compar.c index 00e79b5767dc4c..3635d23e79561f 100644 --- a/compar.c +++ b/compar.c @@ -50,7 +50,7 @@ VALUE rb_invcmp(VALUE x, VALUE y) { VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y); - if (invcmp == Qundef || NIL_P(invcmp)) { + if (UNDEF_P(invcmp) || NIL_P(invcmp)) { return Qnil; } else { diff --git a/compile.c b/compile.c index d8d2738eb8919b..c047408b151042 100644 --- a/compile.c +++ b/compile.c @@ -4820,7 +4820,7 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals, const NODE *val = vals->nd_head; VALUE lit = rb_node_case_when_optimizable_literal(val); - if (lit == Qundef) { + if (UNDEF_P(lit)) { only_special_literals = 0; } else if (NIL_P(rb_hash_lookup(literals, lit))) { @@ -7394,7 +7394,7 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in ADD_LABEL(ret, end_label); ADD_ADJUST_RESTORE(ret, adjust_label); - if (node->nd_state == Qundef) { + if (UNDEF_P(node->nd_state)) { /* ADD_INSN(ret, line_node, putundef); */ COMPILE_ERROR(ERROR_ARGS "unsupported: putundef"); return COMPILE_NG; diff --git a/complex.c b/complex.c index d927f62d5578ec..e9981863c21cb3 100644 --- a/complex.c +++ b/complex.c @@ -561,7 +561,7 @@ nucomp_f_complex(int argc, VALUE *argv, VALUE klass) if (!NIL_P(opts)) { raise = rb_opts_exception_p(opts, raise); } - if (argc > 0 && CLASS_OF(a1) == rb_cComplex && a2 == Qundef) { + if (argc > 0 && CLASS_OF(a1) == rb_cComplex && UNDEF_P(a2)) { return a1; } return nucomp_convert(rb_cComplex, a1, a2, raise); @@ -2107,11 +2107,11 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise) } if (RB_TYPE_P(a1, T_COMPLEX)) { - if (a2 == Qundef || (k_exact_zero_p(a2))) + if (UNDEF_P(a2) || (k_exact_zero_p(a2))) return a1; } - if (a2 == Qundef) { + if (UNDEF_P(a2)) { if (k_numeric_p(a1) && !f_real_p(a1)) return a1; /* should raise exception for consistency */ @@ -2133,7 +2133,7 @@ nucomp_convert(VALUE klass, VALUE a1, VALUE a2, int raise) int argc; VALUE argv2[2]; argv2[0] = a1; - if (a2 == Qundef) { + if (UNDEF_P(a2)) { argv2[1] = Qnil; argc = 1; } diff --git a/configure.ac b/configure.ac index 46caa99e7e39bf..a90a8ff1bb74e9 100644 --- a/configure.ac +++ b/configure.ac @@ -1284,6 +1284,11 @@ dnl AC_HEADER_STDC has been checked in AC_USE_SYSTEM_EXTENSIONS AC_HEADER_STDBOOL AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS([afunix.h], [], [], +[#ifdef _WIN32 +# include +#endif +]) AC_CHECK_HEADERS(atomic.h) AC_CHECK_HEADERS(copyfile.h) AC_CHECK_HEADERS(direct.h) @@ -3824,6 +3829,9 @@ AS_CASE(["${YJIT_SUPPORT}"], LDFLAGS="$LDFLAGS -lpthread -lc++abi" ]) YJIT_OBJ='yjit.$(OBJEXT)' + AS_IF([test x"$YJIT_SUPPORT" != "xyes" ], [ + AC_DEFINE_UNQUOTED(YJIT_SUPPORT, [$YJIT_SUPPORT]) + ]) AC_DEFINE(USE_YJIT, 1) ], [AC_DEFINE(USE_YJIT, 0)]) diff --git a/cont.c b/cont.c index a85b7fc2d1338a..c967e57325dfdb 100644 --- a/cont.c +++ b/cont.c @@ -1831,7 +1831,7 @@ rollback_ensure_stack(VALUE self,rb_ensure_list_t *current,rb_ensure_entry_t *ta /* push ensure stack */ for (j = 0; j < i; j++) { func = lookup_rollback_func(target[i - j - 1].e_proc); - if ((VALUE)func != Qundef) { + if (!UNDEF_P((VALUE)func)) { (*func)(target[i - j - 1].data2); } } @@ -2058,11 +2058,11 @@ rb_fiber_initialize_kw(int argc, VALUE* argv, VALUE self, int kw_splat) argc = rb_scan_args_kw(kw_splat, argc, argv, ":", &options); rb_get_kwargs(options, fiber_initialize_keywords, 0, 2, arguments); - if (arguments[0] != Qundef) { + if (!UNDEF_P(arguments[0])) { blocking = arguments[0]; } - if (arguments[1] != Qundef) { + if (!UNDEF_P(arguments[1])) { pool = arguments[1]; } } diff --git a/dir.c b/dir.c index a35aace6f1d3c6..4d6d80208c169a 100644 --- a/dir.c +++ b/dir.c @@ -2932,7 +2932,7 @@ dir_globs(VALUE args, VALUE base, int flags) static VALUE dir_glob_option_base(VALUE base) { - if (base == Qundef || NIL_P(base)) { + if (UNDEF_P(base) || NIL_P(base)) { return Qnil; } #if USE_OPENDIR_AT @@ -3343,7 +3343,7 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname) result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path, RUBY_UBF_IO, 0); - if (result == Qundef) { + if (UNDEF_P(result)) { rb_sys_fail_path(orig); } return result; diff --git a/doc/net-http/examples.rdoc b/doc/net-http/examples.rdoc new file mode 100644 index 00000000000000..dd4acecda68273 --- /dev/null +++ b/doc/net-http/examples.rdoc @@ -0,0 +1,30 @@ +Examples here assume that net/http has been required +(which also requires +uri+): + + require 'net/http' + +Many code examples here use these example websites: + +- https://jsonplaceholder.typicode.com. +- http://example.com. + +Some examples also assume these variables: + + uri = URI('https://jsonplaceholder.typicode.com') + uri.freeze # Examples may not modify. + hostname = uri.hostname # => "jsonplaceholder.typicode.com" + port = uri.port # => 443 + +So that example requests may be written as: + + Net::HTTP.get(uri) + Net::HTTP.get(hostname, '/index.html') + Net::HTTP.start(hostname) do |http| + http.get('/todos/1') + http.get('/todos/2') + end + +An example that needs a modified URI first duplicates +uri+, then modifies the duplicate: + + _uri = uri.dup + _uri.path = '/todos/1' diff --git a/enum.c b/enum.c index 4a0403faf1afc1..d900083370932c 100644 --- a/enum.c +++ b/enum.c @@ -779,7 +779,7 @@ inject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p)) ENUM_WANT_SVALUE(); - if (memo->v1 == Qundef) { + if (UNDEF_P(memo->v1)) { MEMO_V1_SET(memo, i); } else { @@ -796,7 +796,7 @@ inject_op_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, p)) ENUM_WANT_SVALUE(); - if (memo->v1 == Qundef) { + if (UNDEF_P(memo->v1)) { MEMO_V1_SET(memo, i); } else if (SYMBOL_P(name = memo->u3.value)) { @@ -820,9 +820,9 @@ ary_inject_op(VALUE ary, VALUE init, VALUE op) long i, n; if (RARRAY_LEN(ary) == 0) - return init == Qundef ? Qnil : init; + return UNDEF_P(init) ? Qnil : init; - if (init == Qundef) { + if (UNDEF_P(init)) { v = RARRAY_AREF(ary, 0); i = 1; if (RARRAY_LEN(ary) == 1) @@ -1051,7 +1051,7 @@ enum_inject(int argc, VALUE *argv, VALUE obj) memo = MEMO_NEW(init, Qnil, op); rb_block_call(obj, id_each, 0, 0, iter, (VALUE)memo); - if (memo->v1 == Qundef) return Qnil; + if (UNDEF_P(memo->v1)) return Qnil; return memo->v1; } @@ -1677,7 +1677,7 @@ enum_any(int argc, VALUE *argv, VALUE obj) DEFINE_ENUMFUNCS(one) { if (RTEST(result)) { - if (memo->v1 == Qundef) { + if (UNDEF_P(memo->v1)) { MEMO_V1_SET(memo, Qtrue); } else if (memo->v1 == Qtrue) { @@ -1827,7 +1827,7 @@ nmin_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _data)) else cmpv = i; - if (data->limit != Qundef) { + if (!UNDEF_P(data->limit)) { int c = data->cmpfunc(&cmpv, &data->limit, data); if (data->rev) c = -c; @@ -1962,7 +1962,7 @@ enum_one(int argc, VALUE *argv, VALUE obj) WARN_UNUSED_BLOCK(argc); rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo); result = memo->v1; - if (result == Qundef) return Qfalse; + if (UNDEF_P(result)) return Qfalse; return result; } @@ -2037,7 +2037,7 @@ min_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) ENUM_WANT_SVALUE(); - if (memo->min == Qundef) { + if (UNDEF_P(memo->min)) { memo->min = i; } else { @@ -2056,7 +2056,7 @@ min_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) ENUM_WANT_SVALUE(); - if (memo->min == Qundef) { + if (UNDEF_P(memo->min)) { memo->min = i; } else { @@ -2147,7 +2147,7 @@ enum_min(int argc, VALUE *argv, VALUE obj) rb_block_call(obj, id_each, 0, 0, min_i, memo); } result = m->min; - if (result == Qundef) return Qnil; + if (UNDEF_P(result)) return Qnil; return result; } @@ -2163,7 +2163,7 @@ max_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) ENUM_WANT_SVALUE(); - if (memo->max == Qundef) { + if (UNDEF_P(memo->max)) { memo->max = i; } else { @@ -2182,7 +2182,7 @@ max_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) ENUM_WANT_SVALUE(); - if (memo->max == Qundef) { + if (UNDEF_P(memo->max)) { memo->max = i; } else { @@ -2272,7 +2272,7 @@ enum_max(int argc, VALUE *argv, VALUE obj) rb_block_call(obj, id_each, 0, 0, max_i, (VALUE)memo); } result = m->max; - if (result == Qundef) return Qnil; + if (UNDEF_P(result)) return Qnil; return result; } @@ -2288,7 +2288,7 @@ minmax_i_update(VALUE i, VALUE j, struct minmax_t *memo) { int n; - if (memo->min == Qundef) { + if (UNDEF_P(memo->min)) { memo->min = i; memo->max = j; } @@ -2313,7 +2313,7 @@ minmax_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo)) ENUM_WANT_SVALUE(); - if (memo->last == Qundef) { + if (UNDEF_P(memo->last)) { memo->last = i; return Qnil; } @@ -2340,7 +2340,7 @@ minmax_ii_update(VALUE i, VALUE j, struct minmax_t *memo) { int n; - if (memo->min == Qundef) { + if (UNDEF_P(memo->min)) { memo->min = i; memo->max = j; } @@ -2365,7 +2365,7 @@ minmax_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo)) ENUM_WANT_SVALUE(); - if (memo->last == Qundef) { + if (UNDEF_P(memo->last)) { memo->last = i; return Qnil; } @@ -2430,15 +2430,15 @@ enum_minmax(VALUE obj) m->cmp_opt.opt_inited = 0; if (rb_block_given_p()) { rb_block_call(obj, id_each, 0, 0, minmax_ii, memo); - if (m->last != Qundef) + if (!UNDEF_P(m->last)) minmax_ii_update(m->last, m->last, m); } else { rb_block_call(obj, id_each, 0, 0, minmax_i, memo); - if (m->last != Qundef) + if (!UNDEF_P(m->last)) minmax_i_update(m->last, m->last, m); } - if (m->min != Qundef) { + if (!UNDEF_P(m->min)) { return rb_assoc_new(m->min, m->max); } return rb_assoc_new(Qnil, Qnil); @@ -2454,7 +2454,7 @@ min_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) ENUM_WANT_SVALUE(); v = enum_yield(argc, i); - if (memo->v1 == Qundef) { + if (UNDEF_P(memo->v1)) { MEMO_V1_SET(memo, v); MEMO_V2_SET(memo, i); } @@ -2529,7 +2529,7 @@ max_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) ENUM_WANT_SVALUE(); v = enum_yield(argc, i); - if (memo->v1 == Qundef) { + if (UNDEF_P(memo->v1)) { MEMO_V1_SET(memo, v); MEMO_V2_SET(memo, i); } @@ -2608,7 +2608,7 @@ minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *m { struct cmp_opt_data cmp_opt = { 0, 0 }; - if (memo->min_bv == Qundef) { + if (UNDEF_P(memo->min_bv)) { memo->min_bv = v1; memo->max_bv = v2; memo->min = i1; @@ -2638,7 +2638,7 @@ minmax_by_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo)) vi = enum_yield(argc, i); - if (memo->last_bv == Qundef) { + if (UNDEF_P(memo->last_bv)) { memo->last_bv = vi; memo->last = i; return Qnil; @@ -2705,7 +2705,7 @@ enum_minmax_by(VALUE obj) m->last_bv = Qundef; m->last = Qundef; rb_block_call(obj, id_each, 0, 0, minmax_by_i, memo); - if (m->last_bv != Qundef) + if (!UNDEF_P(m->last_bv)) minmax_by_i_update(m->last_bv, m->last_bv, m->last, m->last, m); m = MEMO_FOR(struct minmax_by_t, memo); return rb_assoc_new(m->min, m->max); @@ -3185,7 +3185,7 @@ zip_i(RB_BLOCK_CALL_FUNC_ARGLIST(val, memoval)) v[1] = RARRAY_AREF(args, i); rb_rescue2(call_next, (VALUE)v, call_stop, (VALUE)v, rb_eStopIteration, (VALUE)0); - if (v[0] == Qundef) { + if (UNDEF_P(v[0])) { RARRAY_ASET(args, i, Qnil); v[0] = Qnil; } @@ -4155,7 +4155,7 @@ slicewhen_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, _memo)) ENUM_WANT_SVALUE(); - if (memo->prev_elt == Qundef) { + if (UNDEF_P(memo->prev_elt)) { /* The first element */ memo->prev_elt = i; memo->prev_elts = rb_ary_new3(1, i); @@ -4395,7 +4395,7 @@ sum_iter_bignum(VALUE i, struct enum_sum_memo *memo) static void sum_iter_rational(VALUE i, struct enum_sum_memo *memo) { - if (memo->r == Qundef) { + if (UNDEF_P(memo->r)) { memo->r = i; } else { @@ -4616,7 +4616,7 @@ enum_sum(int argc, VALUE* argv, VALUE obj) else { if (memo.n != 0) memo.v = rb_fix_plus(LONG2FIX(memo.n), memo.v); - if (memo.r != Qundef) { + if (!UNDEF_P(memo.r)) { memo.v = rb_rational_plus(memo.r, memo.v); } return memo.v; diff --git a/enumerator.c b/enumerator.c index 2c9858cda6d0f5..d41fcfde083f21 100644 --- a/enumerator.c +++ b/enumerator.c @@ -260,7 +260,7 @@ enumerator_ptr(VALUE obj) struct enumerator *ptr; TypedData_Get_Struct(obj, struct enumerator, &enumerator_data_type, ptr); - if (!ptr || ptr->obj == Qundef) { + if (!ptr || UNDEF_P(ptr->obj)) { rb_raise(rb_eArgError, "uninitialized enumerator"); } return ptr; @@ -735,7 +735,7 @@ next_ii(RB_BLOCK_CALL_FUNC_ARGLIST(i, obj)) VALUE feedvalue = Qnil; VALUE args = rb_ary_new4(argc, argv); rb_fiber_yield(1, &args); - if (e->feedvalue != Qundef) { + if (!UNDEF_P(e->feedvalue)) { feedvalue = e->feedvalue; e->feedvalue = Qundef; } @@ -840,7 +840,7 @@ enumerator_next_values(VALUE obj) struct enumerator *e = enumerator_ptr(obj); VALUE vs; - if (e->lookahead != Qundef) { + if (!UNDEF_P(e->lookahead)) { vs = e->lookahead; e->lookahead = Qundef; return vs; @@ -901,7 +901,7 @@ enumerator_peek_values(VALUE obj) { struct enumerator *e = enumerator_ptr(obj); - if (e->lookahead == Qundef) { + if (UNDEF_P(e->lookahead)) { e->lookahead = get_next_values(obj, e); } return e->lookahead; @@ -1025,7 +1025,7 @@ enumerator_feed(VALUE obj, VALUE v) { struct enumerator *e = enumerator_ptr(obj); - if (e->feedvalue != Qundef) { + if (!UNDEF_P(e->feedvalue)) { rb_raise(rb_eTypeError, "feed value already set"); } e->feedvalue = v; @@ -1070,7 +1070,7 @@ inspect_enumerator(VALUE obj, VALUE dummy, int recur) cname = rb_obj_class(obj); - if (!e || e->obj == Qundef) { + if (!e || UNDEF_P(e->obj)) { return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(cname)); } @@ -1239,7 +1239,7 @@ enumerator_size(VALUE obj) argv = RARRAY_CONST_PTR(e->args); } size = rb_check_funcall_kw(e->size, id_call, argc, argv, e->kw_splat); - if (size != Qundef) return size; + if (!UNDEF_P(size)) return size; return e->size; } @@ -1285,7 +1285,7 @@ yielder_ptr(VALUE obj) struct yielder *ptr; TypedData_Get_Struct(obj, struct yielder, &yielder_data_type, ptr); - if (!ptr || ptr->proc == Qundef) { + if (!ptr || UNDEF_P(ptr->proc)) { rb_raise(rb_eArgError, "uninitialized yielder"); } return ptr; @@ -1425,7 +1425,7 @@ generator_ptr(VALUE obj) struct generator *ptr; TypedData_Get_Struct(obj, struct generator, &generator_data_type, ptr); - if (!ptr || ptr->proc == Qundef) { + if (!ptr || UNDEF_P(ptr->proc)) { rb_raise(rb_eArgError, "uninitialized generator"); } return ptr; @@ -1529,7 +1529,7 @@ static VALUE enum_size(VALUE self) { VALUE r = rb_check_funcall(self, id_size, 0, 0); - return (r == Qundef) ? Qnil : r; + return UNDEF_P(r) ? Qnil : r; } static VALUE @@ -1562,7 +1562,7 @@ lazy_init_iterator(RB_BLOCK_CALL_FUNC_ARGLIST(val, m)) result = rb_yield_values2(len, nargv); ALLOCV_END(args); } - if (result == Qundef) rb_iter_break(); + if (UNDEF_P(result)) rb_iter_break(); return Qnil; } @@ -2923,7 +2923,7 @@ producer_ptr(VALUE obj) struct producer *ptr; TypedData_Get_Struct(obj, struct producer, &producer_data_type, ptr); - if (!ptr || ptr->proc == Qundef) { + if (!ptr || UNDEF_P(ptr->proc)) { rb_raise(rb_eArgError, "uninitialized producer"); } return ptr; @@ -2978,7 +2978,7 @@ producer_each_i(VALUE obj) init = ptr->init; proc = ptr->proc; - if (init == Qundef) { + if (UNDEF_P(init)) { curr = Qnil; } else { @@ -3109,7 +3109,7 @@ enum_chain_ptr(VALUE obj) struct enum_chain *ptr; TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr); - if (!ptr || ptr->enums == Qundef) { + if (!ptr || UNDEF_P(ptr->enums)) { rb_raise(rb_eArgError, "uninitialized chain"); } return ptr; @@ -3302,7 +3302,7 @@ inspect_enum_chain(VALUE obj, VALUE dummy, int recur) TypedData_Get_Struct(obj, struct enum_chain, &enum_chain_data_type, ptr); - if (!ptr || ptr->enums == Qundef) { + if (!ptr || UNDEF_P(ptr->enums)) { return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(klass)); } @@ -3431,7 +3431,7 @@ enum_product_ptr(VALUE obj) struct enum_product *ptr; TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr); - if (!ptr || ptr->enums == Qundef) { + if (!ptr || UNDEF_P(ptr->enums)) { rb_raise(rb_eArgError, "uninitialized product"); } return ptr; @@ -3642,7 +3642,7 @@ inspect_enum_product(VALUE obj, VALUE dummy, int recur) TypedData_Get_Struct(obj, struct enum_product, &enum_product_data_type, ptr); - if (!ptr || ptr->enums == Qundef) { + if (!ptr || UNDEF_P(ptr->enums)) { return rb_sprintf("#<%"PRIsVALUE": uninitialized>", rb_class_path(klass)); } diff --git a/error.c b/error.c index 67e81d08a0f193..0ff4b8d6d8e082 100644 --- a/error.c +++ b/error.c @@ -1005,7 +1005,7 @@ rb_check_type(VALUE x, int t) { int xt; - if (RB_UNLIKELY(x == Qundef)) { + if (RB_UNLIKELY(UNDEF_P(x))) { rb_bug(UNDEF_LEAKED); } @@ -1026,7 +1026,7 @@ rb_check_type(VALUE x, int t) void rb_unexpected_type(VALUE x, int t) { - if (RB_UNLIKELY(x == Qundef)) { + if (RB_UNLIKELY(UNDEF_P(x))) { rb_bug(UNDEF_LEAKED); } @@ -1228,7 +1228,7 @@ VALUE rb_get_message(VALUE exc) { VALUE e = rb_check_funcall(exc, id_message, 0, 0); - if (e == Qundef) return Qnil; + if (UNDEF_P(e)) return Qnil; if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e); return e; } @@ -1243,7 +1243,7 @@ rb_get_detailed_message(VALUE exc, VALUE opt) else { e = rb_check_funcall_kw(exc, id_detailed_message, 1, &opt, 1); } - if (e == Qundef) return Qnil; + if (UNDEF_P(e)) return Qnil; if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e); return e; } @@ -1635,15 +1635,15 @@ exc_equal(VALUE exc, VALUE obj) int state; obj = rb_protect(try_convert_to_exception, obj, &state); - if (state || obj == Qundef) { + if (state || UNDEF_P(obj)) { rb_set_errinfo(Qnil); return Qfalse; } if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse; mesg = rb_check_funcall(obj, id_message, 0, 0); - if (mesg == Qundef) return Qfalse; + if (UNDEF_P(mesg)) return Qfalse; backtrace = rb_check_funcall(obj, id_backtrace, 0, 0); - if (backtrace == Qundef) return Qfalse; + if (UNDEF_P(backtrace)) return Qfalse; } else { mesg = rb_attr_get(obj, id_mesg); @@ -1746,7 +1746,7 @@ exit_success_p(VALUE exc) static VALUE err_init_recv(VALUE exc, VALUE recv) { - if (recv != Qundef) rb_ivar_set(exc, id_recv, recv); + if (!UNDEF_P(recv)) rb_ivar_set(exc, id_recv, recv); return exc; } @@ -2090,7 +2090,7 @@ name_err_mesg_to_str(VALUE obj) break; default: d = rb_protect(name_err_mesg_receiver_name, obj, &state); - if (state || d == Qundef || NIL_P(d)) + if (state || UNDEF_P(d) || NIL_P(d)) d = rb_protect(rb_inspect, obj, &state); if (state) { rb_set_errinfo(Qnil); @@ -2145,7 +2145,7 @@ name_err_receiver(VALUE self) VALUE *ptr, recv, mesg; recv = rb_ivar_lookup(self, id_recv, Qundef); - if (recv != Qundef) return recv; + if (!UNDEF_P(recv)) return recv; mesg = rb_attr_get(self, id_mesg); if (!rb_typeddata_is_kind_of(mesg, &name_err_mesg_data_type)) { @@ -2203,7 +2203,7 @@ key_err_receiver(VALUE self) VALUE recv; recv = rb_ivar_lookup(self, id_receiver, Qundef); - if (recv != Qundef) return recv; + if (!UNDEF_P(recv)) return recv; rb_raise(rb_eArgError, "no receiver is available"); } @@ -2220,7 +2220,7 @@ key_err_key(VALUE self) VALUE key; key = rb_ivar_lookup(self, id_key, Qundef); - if (key != Qundef) return key; + if (!UNDEF_P(key)) return key; rb_raise(rb_eArgError, "no key is available"); } @@ -2258,7 +2258,7 @@ key_err_initialize(int argc, VALUE *argv, VALUE self) keywords[1] = id_key; rb_get_kwargs(options, keywords, 0, numberof(values), values); for (i = 0; i < numberof(values); ++i) { - if (values[i] != Qundef) { + if (!UNDEF_P(values[i])) { rb_ivar_set(self, keywords[i], values[i]); } } @@ -2280,7 +2280,7 @@ no_matching_pattern_key_err_matchee(VALUE self) VALUE matchee; matchee = rb_ivar_lookup(self, id_matchee, Qundef); - if (matchee != Qundef) return matchee; + if (!UNDEF_P(matchee)) return matchee; rb_raise(rb_eArgError, "no matchee is available"); } @@ -2297,7 +2297,7 @@ no_matching_pattern_key_err_key(VALUE self) VALUE key; key = rb_ivar_lookup(self, id_key, Qundef); - if (key != Qundef) return key; + if (!UNDEF_P(key)) return key; rb_raise(rb_eArgError, "no key is available"); } @@ -2324,7 +2324,7 @@ no_matching_pattern_key_err_initialize(int argc, VALUE *argv, VALUE self) keywords[1] = id_key; rb_get_kwargs(options, keywords, 0, numberof(values), values); for (i = 0; i < numberof(values); ++i) { - if (values[i] != Qundef) { + if (!UNDEF_P(values[i])) { rb_ivar_set(self, keywords[i], values[i]); } } diff --git a/eval.c b/eval.c index 826ae1456e5826..c95c3fb9edaf5e 100644 --- a/eval.c +++ b/eval.c @@ -514,7 +514,7 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause) nocause = 0; nocircular = 1; } - if (*cause == Qundef) { + if (UNDEF_P(*cause)) { if (nocause) { *cause = Qnil; nocircular = 1; @@ -530,7 +530,7 @@ exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause) rb_raise(rb_eTypeError, "exception object expected"); } - if (!nocircular && !NIL_P(*cause) && *cause != Qundef && *cause != mesg) { + if (!nocircular && !NIL_P(*cause) && !UNDEF_P(*cause) && *cause != mesg) { VALUE c = *cause; while (!NIL_P(c = rb_attr_get(c, id_cause))) { if (c == mesg) { @@ -549,18 +549,18 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE const char *file = rb_source_location_cstr(&line); const char *const volatile file0 = file; - if ((file && !NIL_P(mesg)) || (cause != Qundef)) { + if ((file && !NIL_P(mesg)) || !UNDEF_P(cause)) { volatile int state = 0; EC_PUSH_TAG(ec); if (EC_EXEC_TAG() == TAG_NONE && !(state = rb_ec_set_raised(ec))) { VALUE bt = rb_get_backtrace(mesg); - if (!NIL_P(bt) || cause == Qundef) { + if (!NIL_P(bt) || UNDEF_P(cause)) { if (OBJ_FROZEN(mesg)) { mesg = rb_obj_dup(mesg); } } - if (cause != Qundef && !THROW_DATA_P(cause)) { + if (!UNDEF_P(cause) && !THROW_DATA_P(cause)) { exc_setup_cause(mesg, cause); } if (NIL_P(bt)) { @@ -633,7 +633,7 @@ setup_exception(rb_execution_context_t *ec, int tag, volatile VALUE mesg, VALUE void rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause) { - if (cause == Qundef) { + if (UNDEF_P(cause)) { cause = get_ec_errinfo(ec); } if (cause != mesg) { @@ -728,7 +728,7 @@ rb_f_raise(int argc, VALUE *argv) argc = extract_raise_opts(argc, argv, opts); if (argc == 0) { - if (*cause != Qundef) { + if (!UNDEF_P(*cause)) { rb_raise(rb_eArgError, "only cause is given with no arguments"); } err = get_errinfo(); @@ -804,7 +804,7 @@ make_exception(int argc, const VALUE *argv, int isstr) if (NIL_P(mesg)) { mesg = rb_check_funcall(argv[0], idException, argc != 1, &argv[1]); } - if (mesg == Qundef) { + if (UNDEF_P(mesg)) { rb_raise(rb_eTypeError, "exception class/object expected"); } if (!rb_obj_is_kind_of(mesg, rb_eException)) { diff --git a/eval_error.c b/eval_error.c index 6582b805aa07dd..dfb3e6b6762e5d 100644 --- a/eval_error.c +++ b/eval_error.c @@ -306,7 +306,7 @@ rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VA if (NIL_P(errinfo)) return; - if (errat == Qundef) { + if (UNDEF_P(errat)) { errat = Qnil; } eclass = CLASS_OF(errinfo); @@ -358,7 +358,7 @@ rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo) if (EC_EXEC_TAG() == TAG_NONE) { errat = rb_get_backtrace(errinfo); } - if (emesg == Qundef) { + if (UNDEF_P(emesg)) { emesg = Qnil; emesg = rb_get_detailed_message(errinfo, opt); } diff --git a/ext/extmk.rb b/ext/extmk.rb index 73ee71fd376241..cc560be1f22c93 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -721,6 +721,8 @@ def initialize(src) mf.puts "ECHO1 = $(V:1=@:)" mf.puts "ECHO = $(ECHO1:0=@echo)" mf.puts "MFLAGS = -$(MAKEFLAGS)" if $nmake + mf.puts "override MFLAGS := $(filter-out -j%,$(MFLAGS))" if $gnumake + mf.puts "ext_build_dir = #{File.dirname($command_output)}" mf.puts def mf.macro(name, values, max = 70) @@ -763,6 +765,7 @@ def mf.macro(name, values, max = 70) mf.macro "SUBMAKEOPTS", submakeopts mf.macro "NOTE_MESG", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb skip] mf.macro "NOTE_NAME", %w[$(RUBY) $(top_srcdir)/tool/lib/colorize.rb fail] + %w[RM RMDIRS RMDIR RMALL].each {|w| mf.macro w, [RbConfig::CONFIG[w]]} mf.puts targets = %w[all install static install-so install-rb clean distclean realclean] targets.each do |tgt| @@ -797,16 +800,20 @@ def mf.macro(name, values, max = 70) exts.each do |d| d = d[0..-2] t = "#{d}#{tgt}" - if /^(dist|real)?clean$/ =~ tgt + if clean = /^(dist|real)?clean$/.match(tgt) deps = exts.select {|e|e.start_with?(d)}.map {|e|"#{e[0..-2]}#{tgt}"} - [t] - pd = ' ' + deps.join(' ') unless deps.empty? + pd = [' clean-local', *deps].join(' ') else pext = File.dirname(d) pd = " #{pext}/#{tgt}" if exts.include?("#{pext}/.") end mf.puts "#{t}:#{pd}\n\t$(Q)#{submake} $(MFLAGS) V=$(V) $(@F)" + if clean and clean.begin(1) + mf.puts "\t$(Q)$(RM) $(ext_build_dir)/exts.mk\n\t$(Q)$(RMDIRS) -p $(@D)" + end end end + mf.puts "\n""clean-local:\n\t$(Q)$(RM) $(ext_build_dir)/*~ $(ext_build_dir)/*.bak $(ext_build_dir)/core" mf.puts "\n""extso:\n" mf.puts "\t@echo EXTSO=$(EXTSO)" diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb index 8998bb5c2f2869..73bbc8e6871436 100644 --- a/ext/socket/extconf.rb +++ b/ext/socket/extconf.rb @@ -316,6 +316,7 @@ def test_recvmsg_with_msg_peek_creates_fds(headers) netpacket/packet.h net/ethernet.h sys/un.h + afunix.h ifaddrs.h sys/ioctl.h sys/sockio.h diff --git a/ext/socket/init.c b/ext/socket/init.c index ac28f5c3293377..557d4374a51e0c 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -209,7 +209,7 @@ rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from) else return rb_assoc_new(str, Qnil); -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case RECV_UNIX: return rb_assoc_new(str, rsock_unixaddr(&arg.buf.un, arg.alen)); #endif diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb index d756a32a5aafb0..c96e48e8f0429c 100644 --- a/ext/socket/lib/socket.rb +++ b/ext/socket/lib/socket.rb @@ -197,7 +197,7 @@ def listen(backlog=Socket::SOMAXCONN) sock = Socket.new(self.pfamily, self.socktype, self.protocol) begin sock.ipv6only! if self.ipv6? - sock.setsockopt(:SOCKET, :REUSEADDR, 1) + sock.setsockopt(:SOCKET, :REUSEADDR, 1) unless self.pfamily == Socket::PF_UNIX sock.bind(self) sock.listen(backlog) rescue Exception diff --git a/ext/socket/option.c b/ext/socket/option.c index 2dbe6379c43674..0d818d0c704520 100644 --- a/ext/socket/option.c +++ b/ext/socket/option.c @@ -670,10 +670,10 @@ rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int { #if defined(HAVE_IF_INDEXTONAME) char ifbuf[IFNAMSIZ]; - if (if_indextoname(ifindex, ifbuf) == NULL) - return snprintf(buf, len, "%s%u", fail_prefix, ifindex); - else + if (if_indextoname(ifindex, ifbuf)) return snprintf(buf, len, "%s%s", succ_prefix, ifbuf); + else + return snprintf(buf, len, "%s%u", fail_prefix, ifindex); #else # ifndef IFNAMSIZ # define IFNAMSIZ (sizeof(unsigned int)*3+1) @@ -1229,7 +1229,7 @@ sockopt_inspect(VALUE self) else rb_str_catf(ret, " optname:%d", optname); } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN else if (family == AF_UNIX) { rb_str_catf(ret, " level:%d", level); @@ -1393,7 +1393,7 @@ sockopt_inspect(VALUE self) } break; -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: switch (level) { case 0: diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index 636d1edda3296c..269edc4dadd526 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -644,7 +644,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup) return ary; } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN static long unixsocket_len(const struct sockaddr_un *su, socklen_t socklen) { @@ -1017,7 +1017,7 @@ addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN static void init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype) { @@ -1146,7 +1146,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) break; } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: /* ["AF_UNIX", "/tmp/sock"] */ { VALUE path = rb_ary_entry(sockaddr_ary, 1); @@ -1286,7 +1286,7 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r } #endif -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: { struct sockaddr_un *addr = &sockaddr->un; @@ -1622,7 +1622,7 @@ addrinfo_mdump(VALUE self) afamily = rb_id2str(id); switch(afamily_int) { -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: { sockaddr = rb_str_new(rai->addr.un.sun_path, rai_unixsocket_len(rai)); @@ -1715,7 +1715,7 @@ addrinfo_mload(VALUE self, VALUE ary) v = rb_ary_entry(ary, 1); switch(afamily) { -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: { struct sockaddr_un uaddr; @@ -2343,7 +2343,7 @@ addrinfo_ipv6_to_ipv4(VALUE self) #endif -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * call-seq: * addrinfo.unix_path => path @@ -2491,7 +2491,7 @@ addrinfo_s_udp(VALUE self, VALUE host, VALUE port) INT2NUM(PF_UNSPEC), INT2NUM(SOCK_DGRAM), INT2NUM(IPPROTO_UDP), INT2FIX(0)); } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * call-seq: @@ -2629,7 +2629,7 @@ rsock_init_addrinfo(void) rb_define_singleton_method(rb_cAddrinfo, "ip", addrinfo_s_ip, 1); rb_define_singleton_method(rb_cAddrinfo, "tcp", addrinfo_s_tcp, 2); rb_define_singleton_method(rb_cAddrinfo, "udp", addrinfo_s_udp, 2); -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN rb_define_singleton_method(rb_cAddrinfo, "unix", addrinfo_s_unix, -1); #endif @@ -2670,7 +2670,7 @@ rsock_init_addrinfo(void) rb_define_method(rb_cAddrinfo, "ipv6_to_ipv4", addrinfo_ipv6_to_ipv4, 0); #endif -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN rb_define_method(rb_cAddrinfo, "unix_path", addrinfo_unix_path, 0); #endif diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h index 9ec893ee8ccc6f..5f803ba0da3c6f 100644 --- a/ext/socket/rubysocket.h +++ b/ext/socket/rubysocket.h @@ -33,6 +33,9 @@ #endif #ifdef _WIN32 +# include +# include +# include # if defined(_MSC_VER) # undef HAVE_TYPE_STRUCT_SOCKADDR_DL # endif @@ -69,6 +72,11 @@ # include #endif +#ifdef HAVE_AFUNIX_H +// Windows doesn't have sys/un.h, but it does have afunix.h just to be special: +# include +#endif + #if defined(HAVE_FCNTL) # ifdef HAVE_SYS_SELECT_H # include @@ -268,7 +276,7 @@ extern VALUE rb_cIPSocket; extern VALUE rb_cTCPSocket; extern VALUE rb_cTCPServer; extern VALUE rb_cUDPSocket; -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN extern VALUE rb_cUNIXSocket; extern VALUE rb_cUNIXServer; #endif @@ -336,7 +344,7 @@ VALUE rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len); int rsock_revlookup_flag(VALUE revlookup, int *norevlookup); -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN VALUE rsock_unixpath_str(struct sockaddr_un *sockaddr, socklen_t len); VALUE rsock_unixaddr(struct sockaddr_un *sockaddr, socklen_t len); socklen_t rsock_unix_sockaddr_len(VALUE path); diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 5cf083506252e9..eb74f7a936a3f3 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -1383,7 +1383,7 @@ sock_s_unpack_sockaddr_in(VALUE self, VALUE addr) return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host); } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * call-seq: @@ -1471,7 +1471,7 @@ sockaddr_len(struct sockaddr *addr) return (socklen_t)sizeof(struct sockaddr_in6); #endif -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: return (socklen_t)sizeof(struct sockaddr_un); #endif @@ -2020,7 +2020,7 @@ Init_socket(void) rb_define_singleton_method(rb_cSocket, "sockaddr_in", sock_s_pack_sockaddr_in, 2); rb_define_singleton_method(rb_cSocket, "pack_sockaddr_in", sock_s_pack_sockaddr_in, 2); rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_in", sock_s_unpack_sockaddr_in, 1); -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN rb_define_singleton_method(rb_cSocket, "sockaddr_un", sock_s_pack_sockaddr_un, 1); rb_define_singleton_method(rb_cSocket, "pack_sockaddr_un", sock_s_pack_sockaddr_un, 1); rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_un", sock_s_unpack_sockaddr_un, 1); diff --git a/ext/socket/unixserver.c b/ext/socket/unixserver.c index 3a899cca1fbbad..0ea5ac083c3ca8 100644 --- a/ext/socket/unixserver.c +++ b/ext/socket/unixserver.c @@ -10,7 +10,7 @@ #include "rubysocket.h" -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * call-seq: * UNIXServer.new(path) => unixserver @@ -101,7 +101,7 @@ unix_sysaccept(VALUE server) void rsock_init_unixserver(void) { -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * Document-class: UNIXServer < UNIXSocket * diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c index ecffbd4e922be8..26ab76fc9fb7e6 100644 --- a/ext/socket/unixsocket.c +++ b/ext/socket/unixsocket.c @@ -10,7 +10,7 @@ #include "rubysocket.h" -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN struct unixsock_arg { struct sockaddr_un *sockaddr; socklen_t sockaddrlen; @@ -42,6 +42,10 @@ unixsock_path_value(VALUE path) return name; /* ignore encoding */ } } +#endif +#ifdef _WIN32 + /* UNIXSocket requires UTF-8 per spec. */ + path = rb_str_export_to_enc(path, rb_utf8_encoding()); #endif return rb_get_path(path); } @@ -571,7 +575,7 @@ unix_s_socketpair(int argc, VALUE *argv, VALUE klass) void rsock_init_unixsocket(void) { -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * Document-class: UNIXSocket < BasicSocket * diff --git a/file.c b/file.c index 93f5898ccde0b8..10eb1081e328a9 100644 --- a/file.c +++ b/file.c @@ -115,6 +115,8 @@ int flock(int, int); # define link(f, t) rb_w32_ulink((f), (t)) # undef unlink # define unlink(p) rb_w32_uunlink(p) +# undef readlink +# define readlink(f, t, l) rb_w32_ureadlink((f), (t), (l)) # undef rename # define rename(f, t) rb_w32_urename((f), (t)) # undef symlink @@ -1763,8 +1765,8 @@ rb_file_socket_p(VALUE obj, VALUE fname) if (rb_stat(fname, &st) < 0) return Qfalse; if (S_ISSOCK(st.st_mode)) return Qtrue; - #endif + return Qfalse; } @@ -3152,7 +3154,6 @@ rb_file_s_readlink(VALUE klass, VALUE path) return rb_readlink(path, rb_filesystem_encoding()); } -#ifndef _WIN32 struct readlink_arg { const char *path; char *buf; @@ -3208,7 +3209,6 @@ rb_readlink(VALUE path, rb_encoding *enc) return v; } -#endif #else #define rb_file_s_readlink rb_f_notimplement #endif @@ -5600,6 +5600,7 @@ rb_stat_init(VALUE obj, VALUE fname) if (STAT(StringValueCStr(fname), &st) == -1) { rb_sys_fail_path(fname); } + if (DATA_PTR(obj)) { xfree(DATA_PTR(obj)); DATA_PTR(obj) = NULL; diff --git a/gc.c b/gc.c index ef0532e3256d57..8817538194a7d4 100644 --- a/gc.c +++ b/gc.c @@ -3135,6 +3135,12 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) #endif VALUE obj = newobj_of(klass, flags, 0, 0, 0, wb_protected, size); + RUBY_ASSERT(rb_shape_get_shape(obj)->type == SHAPE_ROOT || + rb_shape_get_shape(obj)->type == SHAPE_INITIAL_CAPACITY); + + // Set the shape to the specific T_OBJECT shape which is always + // SIZE_POOL_COUNT away from the root shape. + ROBJECT_SET_SHAPE_ID(obj, ROBJECT_SHAPE_ID(obj) + SIZE_POOL_COUNT); #if RUBY_DEBUG VALUE *ptr = ROBJECT_IVPTR(obj); @@ -4568,7 +4574,7 @@ run_single_final(VALUE cmd, VALUE objid) static void warn_exception_in_finalizer(rb_execution_context_t *ec, VALUE final) { - if (final != Qundef && !NIL_P(ruby_verbose)) { + if (!UNDEF_P(final) && !NIL_P(ruby_verbose)) { VALUE errinfo = ec->errinfo; rb_warn("Exception in finalizer %+"PRIsVALUE, final); rb_ec_error_print(ec, errinfo); @@ -5038,7 +5044,7 @@ id2ref(VALUE objid) } } - if ((orig = id2ref_obj_tbl(objspace, objid)) != Qundef && + if (!UNDEF_P(orig = id2ref_obj_tbl(objspace, objid)) && is_live_object(objspace, orig)) { if (!rb_multi_ractor_p() || rb_ractor_shareable_p(orig)) { @@ -7785,7 +7791,7 @@ gc_mark_stacked_objects(rb_objspace_t *objspace, int incremental, size_t count) #endif while (pop_mark_stack(mstack, &obj)) { - if (obj == Qundef) continue; /* skip */ + if (UNDEF_P(obj)) continue; /* skip */ if (RGENGC_CHECK_MODE && !RVALUE_MARKED(obj)) { rb_bug("gc_mark_stacked_objects: %s is not marked.", obj_info(obj)); @@ -10492,7 +10498,7 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v) } ptr = ROBJECT(v)->as.ary; size_t size_pool_shape_id = size_pool_idx_for_size(embed_size); - rb_shape_t * initial_shape = rb_shape_get_shape_by_id((shape_id_t)size_pool_shape_id); + rb_shape_t * initial_shape = rb_shape_get_shape_by_id((shape_id_t)size_pool_shape_id + SIZE_POOL_COUNT); rb_shape_t * new_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_shape(v)); rb_shape_set_shape(v, new_shape); } @@ -13301,7 +13307,7 @@ wmap_finalize(RB_BLOCK_CALL_FUNC_ARGLIST(objid, self)) TypedData_Get_Struct(self, struct weakmap, &weakmap_type, w); /* Get reference from object id. */ - if ((obj = id2ref_obj_tbl(&rb_objspace, objid)) == Qundef) { + if (UNDEF_P(obj = id2ref_obj_tbl(&rb_objspace, objid))) { rb_bug("wmap_finalize: objid is not found."); } @@ -13592,14 +13598,14 @@ static VALUE wmap_aref(VALUE self, VALUE key) { VALUE obj = wmap_lookup(self, key); - return obj != Qundef ? obj : Qnil; + return !UNDEF_P(obj) ? obj : Qnil; } /* Returns +true+ if +key+ is registered */ static VALUE wmap_has_key(VALUE self, VALUE key) { - return RBOOL(wmap_lookup(self, key) != Qundef); + return RBOOL(!UNDEF_P(wmap_lookup(self, key))); } /* Returns the number of referenced objects */ diff --git a/hash.c b/hash.c index 876d8b9efc70c0..a577e7798584d6 100644 --- a/hash.c +++ b/hash.c @@ -111,7 +111,7 @@ rb_any_cmp(VALUE a, VALUE b) RB_TYPE_P(b, T_STRING) && RBASIC(b)->klass == rb_cString) { return rb_str_hash_cmp(a, b); } - if (a == Qundef || b == Qundef) return -1; + if (UNDEF_P(a) || UNDEF_P(b)) return -1; if (SYMBOL_P(a) && SYMBOL_P(b)) { return a != b; } @@ -195,7 +195,7 @@ obj_any_hash(VALUE obj) { VALUE hval = rb_check_funcall_basic_kw(obj, id_hash, rb_mKernel, 0, 0, 0); - if (hval == Qundef) { + if (UNDEF_P(hval)) { hval = rb_exec_recursive_outer_mid(hash_recursive, obj, 0, id_hash); } @@ -437,7 +437,7 @@ ar_cleared_entry(VALUE hash, unsigned int index) * so you need to check key == Qundef */ ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, index); - return pair->key == Qundef; + return UNDEF_P(pair->key); } else { return FALSE; @@ -517,8 +517,8 @@ hash_verify_(VALUE hash, const char *file, int line) ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i); k = pair->key; v = pair->val; - HASH_ASSERT(k != Qundef); - HASH_ASSERT(v != Qundef); + HASH_ASSERT(!UNDEF_P(k)); + HASH_ASSERT(!UNDEF_P(v)); n++; } } @@ -2076,7 +2076,7 @@ rb_hash_default_value(VALUE hash, VALUE key) if (LIKELY(rb_method_basic_definition_p(CLASS_OF(hash), id_default))) { VALUE ifnone = RHASH_IFNONE(hash); if (!FL_TEST(hash, RHASH_PROC_DEFAULT)) return ifnone; - if (key == Qundef) return Qnil; + if (UNDEF_P(key)) return Qnil; return call_default_proc(ifnone, hash, key); } else { @@ -2402,7 +2402,7 @@ rb_hash_delete(VALUE hash, VALUE key) { VALUE deleted_value = rb_hash_delete_entry(hash, key); - if (deleted_value != Qundef) { /* likely pass */ + if (!UNDEF_P(deleted_value)) { /* likely pass */ return deleted_value; } else { @@ -2445,7 +2445,7 @@ rb_hash_delete_m(VALUE hash, VALUE key) rb_hash_modify_check(hash); val = rb_hash_delete_entry(hash, key); - if (val != Qundef) { + if (!UNDEF_P(val)) { return val; } else { @@ -2502,7 +2502,7 @@ rb_hash_shift(VALUE hash) } else { rb_hash_foreach(hash, shift_i_safe, (VALUE)&var); - if (var.key != Qundef) { + if (!UNDEF_P(var.key)) { rb_hash_delete_entry(hash, var.key); return rb_assoc_new(var.key, var.val); } @@ -2517,7 +2517,7 @@ rb_hash_shift(VALUE hash) } else { rb_hash_foreach(hash, shift_i_safe, (VALUE)&var); - if (var.key != Qundef) { + if (!UNDEF_P(var.key)) { rb_hash_delete_entry(hash, var.key); return rb_assoc_new(var.key, var.val); } @@ -2658,7 +2658,7 @@ rb_hash_slice(int argc, VALUE *argv, VALUE hash) for (i = 0; i < argc; i++) { key = argv[i]; value = rb_hash_lookup2(hash, key, Qundef); - if (value != Qundef) + if (!UNDEF_P(value)) rb_hash_aset(result, key, value); } @@ -3181,7 +3181,7 @@ transform_keys_hash_i(VALUE key, VALUE value, VALUE transarg) struct transform_keys_args *p = (void *)transarg; VALUE trans = p->trans, result = p->result; VALUE new_key = rb_hash_lookup2(trans, key, Qundef); - if (new_key == Qundef) { + if (UNDEF_P(new_key)) { if (p->block_given) new_key = rb_yield(key); else @@ -3302,7 +3302,7 @@ rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash) if (!trans) { new_key = rb_yield(key); } - else if ((new_key = rb_hash_lookup2(trans, key, Qundef)) != Qundef) { + else if (!UNDEF_P(new_key = rb_hash_lookup2(trans, key, Qundef))) { /* use the transformed key */ } else if (block_given) { @@ -4195,7 +4195,7 @@ rb_hash_assoc(VALUE hash, VALUE key) ensure_arg.hash = hash; ensure_arg.orighash = orighash; value = rb_ensure(lookup2_call, (VALUE)&args, reset_hash_type, (VALUE)&ensure_arg); - if (value != Qundef) return rb_assoc_new(key, value); + if (!UNDEF_P(value)) return rb_assoc_new(key, value); } args[0] = key; @@ -4608,7 +4608,7 @@ hash_le_i(VALUE key, VALUE value, VALUE arg) { VALUE *args = (VALUE *)arg; VALUE v = rb_hash_lookup2(args[0], key, Qundef); - if (v != Qundef && rb_equal(value, v)) return ST_CONTINUE; + if (!UNDEF_P(v) && rb_equal(value, v)) return ST_CONTINUE; args[1] = Qfalse; return ST_STOP; } diff --git a/include/ruby/internal/special_consts.h b/include/ruby/internal/special_consts.h index ed2428e087d070..dc0a6b41d6bad6 100644 --- a/include/ruby/internal/special_consts.h +++ b/include/ruby/internal/special_consts.h @@ -326,7 +326,7 @@ RBIMPL_ATTR_ARTIFICIAL() static inline bool RB_SPECIAL_CONST_P(VALUE obj) { - return RB_IMMEDIATE_P(obj) || ! RB_TEST(obj); + return RB_IMMEDIATE_P(obj) || obj == RUBY_Qfalse; } RBIMPL_ATTR_CONST() diff --git a/include/ruby/win32.h b/include/ruby/win32.h index 93e6183ed990a2..197eb8a8027ee4 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -18,11 +18,6 @@ RUBY_SYMBOL_EXPORT_BEGIN * */ -/* - * Definitions for NT port of Perl - */ - - /* * Ok now we can include the normal include files. */ @@ -302,7 +297,6 @@ extern DWORD rb_w32_osver(void); extern int rb_w32_uchown(const char *, int, int); extern int rb_w32_ulink(const char *, const char *); extern ssize_t rb_w32_ureadlink(const char *, char *, size_t); -extern ssize_t rb_w32_wreadlink(const WCHAR *, WCHAR *, size_t); extern int rb_w32_usymlink(const char *src, const char *link); extern int gettimeofday(struct timeval *, struct timezone *); extern int clock_gettime(clockid_t, struct timespec *); @@ -393,6 +387,7 @@ scalb(double a, long b) #endif #define S_IFLNK 0xa000 +#define S_IFSOCK 0xc000 /* * define this so we can do inplace editing diff --git a/inits.c b/inits.c index 3c2b11c851df43..17fa0afbb677f4 100644 --- a/inits.c +++ b/inits.c @@ -99,6 +99,7 @@ rb_call_builtin_inits(void) BUILTIN(warning); BUILTIN(array); BUILTIN(kernel); + BUILTIN(symbol); BUILTIN(timev); BUILTIN(thread_sync); BUILTIN(yjit); diff --git a/io.c b/io.c index 63a96bf2b80a68..8787c4f804fac5 100644 --- a/io.c +++ b/io.c @@ -1255,7 +1255,7 @@ rb_io_read_memory(rb_io_t *fptr, void *buf, size_t count) if (scheduler != Qnil) { VALUE result = rb_fiber_scheduler_io_read_memory(scheduler, fptr->self, buf, count, 0); - if (result != Qundef) { + if (!UNDEF_P(result)) { return rb_fiber_scheduler_io_result_apply(result); } } @@ -1288,7 +1288,7 @@ rb_io_write_memory(rb_io_t *fptr, const void *buf, size_t count) if (scheduler != Qnil) { VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, buf, count, 0); - if (result != Qundef) { + if (!UNDEF_P(result)) { return rb_fiber_scheduler_io_result_apply(result); } } @@ -1323,7 +1323,7 @@ rb_writev_internal(rb_io_t *fptr, const struct iovec *iov, int iovcnt) for (int i = 0; i < iovcnt; i += 1) { VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, iov[i].iov_base, iov[i].iov_len, 0); - if (result != Qundef) { + if (!UNDEF_P(result)) { return rb_fiber_scheduler_io_result_apply(result); } } @@ -1424,7 +1424,7 @@ rb_io_wait(VALUE io, VALUE events, VALUE timeout) struct timeval tv_storage; struct timeval *tv = NULL; - if (timeout == Qnil || timeout == Qundef) { + if (timeout == Qnil || UNDEF_P(timeout)) { timeout = fptr->timeout; } @@ -3352,7 +3352,7 @@ io_read_memory_call(VALUE arg) if (scheduler != Qnil) { VALUE result = rb_fiber_scheduler_io_read_memory(scheduler, iis->fptr->self, iis->buf, iis->capa, 0); - if (result != Qundef) { + if (!UNDEF_P(result)) { // This is actually returned as a pseudo-VALUE and later cast to a long: return (VALUE)rb_fiber_scheduler_io_result_apply(result); } @@ -3972,7 +3972,7 @@ extract_getline_opts(VALUE opts, struct getline_arg *args) kwds[0] = rb_intern_const("chomp"); } rb_get_kwargs(opts, kwds, 0, -2, &vchomp); - chomp = (vchomp != Qundef) && RTEST(vchomp); + chomp = (!UNDEF_P(vchomp)) && RTEST(vchomp); } args->chomp = chomp; } @@ -5445,7 +5445,7 @@ fptr_finalize_flush(rb_io_t *fptr, int noraise, int keepgvl, // VALUE scheduler = rb_fiber_scheduler_current(); // if (scheduler != Qnil) { // VALUE result = rb_fiber_scheduler_io_close(scheduler, fptr->self); - // if (result != Qundef) done = 1; + // if (!UNDEF_P(result)) done = 1; // } // } @@ -5704,7 +5704,7 @@ static VALUE io_close(VALUE io) { VALUE closed = rb_check_funcall(io, rb_intern("closed?"), 0, 0); - if (closed != Qundef && RTEST(closed)) return io; + if (!UNDEF_P(closed) && RTEST(closed)) return io; rb_rescue2(io_call_close, io, ignore_closed_stream, io, rb_eIOError, (VALUE)0); return io; @@ -6622,21 +6622,21 @@ rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2 v = rb_hash_lookup2(opt, sym_extenc, Qundef); if (v != Qnil) extenc = v; v = rb_hash_lookup2(opt, sym_intenc, Qundef); - if (v != Qundef) intenc = v; + if (!UNDEF_P(v)) intenc = v; } - if ((extenc != Qundef || intenc != Qundef) && !NIL_P(encoding)) { + if ((!UNDEF_P(extenc) || !UNDEF_P(intenc)) && !NIL_P(encoding)) { if (!NIL_P(ruby_verbose)) { int idx = rb_to_encoding_index(encoding); if (idx >= 0) encoding = rb_enc_from_encoding(rb_enc_from_index(idx)); rb_warn("Ignoring encoding parameter '%"PRIsVALUE"': %s_encoding is used", - encoding, extenc == Qundef ? "internal" : "external"); + encoding, UNDEF_P(extenc) ? "internal" : "external"); } encoding = Qnil; } - if (extenc != Qundef && !NIL_P(extenc)) { + if (!UNDEF_P(extenc) && !NIL_P(extenc)) { extencoding = rb_to_encoding(extenc); } - if (intenc != Qundef) { + if (!UNDEF_P(intenc)) { if (NIL_P(intenc)) { /* internal_encoding: nil => no transcoding */ intencoding = (rb_encoding *)Qnil; @@ -6669,7 +6669,7 @@ rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2 rb_io_ext_int_to_encs(rb_to_encoding(encoding), NULL, enc_p, enc2_p, 0); } } - else if (extenc != Qundef || intenc != Qundef) { + else if (!UNDEF_P(extenc) || !UNDEF_P(intenc)) { extracted = 1; rb_io_ext_int_to_encs(extencoding, intencoding, enc_p, enc2_p, 0); } @@ -9724,7 +9724,7 @@ io_wait(int argc, VALUE *argv, VALUE io) if (RB_SYMBOL_P(argv[i])) { events |= wait_mode_sym(argv[i]); } - else if (timeout == Qundef) { + else if (UNDEF_P(timeout)) { rb_time_interval(timeout = argv[i]); } else { @@ -9732,7 +9732,7 @@ io_wait(int argc, VALUE *argv, VALUE io) } } - if (timeout == Qundef) timeout = Qnil; + if (UNDEF_P(timeout)) timeout = Qnil; if (events == 0) { events = RUBY_IO_READABLE; @@ -10918,7 +10918,7 @@ rb_f_select(int argc, VALUE *argv, VALUE obj) if (scheduler != Qnil) { // It's optionally supported. VALUE result = rb_fiber_scheduler_io_selectv(scheduler, argc, argv); - if (result != Qundef) return result; + if (!UNDEF_P(result)) return result; } VALUE timeout; @@ -14042,7 +14042,7 @@ static void argf_block_call(ID mid, int argc, VALUE *argv, VALUE argf) { VALUE ret = ARGF_block_call(mid, argc, argv, argf_block_call_i, argf); - if (ret != Qundef) ARGF.next_p = 1; + if (!UNDEF_P(ret)) ARGF.next_p = 1; } static VALUE @@ -14058,7 +14058,7 @@ static void argf_block_call_line(ID mid, int argc, VALUE *argv, VALUE argf) { VALUE ret = ARGF_block_call(mid, argc, argv, argf_block_call_line_i, argf); - if (ret != Qundef) ARGF.next_p = 1; + if (!UNDEF_P(ret)) ARGF.next_p = 1; } /* diff --git a/io_buffer.c b/io_buffer.c index cbf51250c4b22d..dc9d613524d5ea 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -2384,7 +2384,7 @@ rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset) if (scheduler != Qnil) { VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, SIZET2NUM(length), SIZET2NUM(offset)); - if (result != Qundef) { + if (!UNDEF_P(result)) { return result; } } @@ -2501,7 +2501,7 @@ rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t of if (scheduler != Qnil) { VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, OFFT2NUM(from), self, SIZET2NUM(length), SIZET2NUM(offset)); - if (result != Qundef) { + if (!UNDEF_P(result)) { return result; } } @@ -2574,7 +2574,7 @@ rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset) if (scheduler != Qnil) { VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, SIZET2NUM(length), SIZET2NUM(offset)); - if (result != Qundef) { + if (!UNDEF_P(result)) { return result; } } @@ -2668,7 +2668,7 @@ rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t o if (scheduler != Qnil) { VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, OFFT2NUM(from), self, SIZET2NUM(length), SIZET2NUM(offset)); - if (result != Qundef) { + if (!UNDEF_P(result)) { return result; } } diff --git a/iseq.c b/iseq.c index 1d0c96dba17d59..a2e79cd55badcf 100644 --- a/iseq.c +++ b/iseq.c @@ -331,7 +331,7 @@ rb_iseq_update_references(rb_iseq_t *iseq) for (j = 0; i < body->param.keyword->num; i++, j++) { VALUE obj = body->param.keyword->default_values[j]; - if (obj != Qundef) { + if (!UNDEF_P(obj)) { body->param.keyword->default_values[j] = rb_gc_location(obj); } } @@ -2916,7 +2916,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq) } for (j=0; inum; i++, j++) { VALUE key = rb_ary_new_from_args(1, ID2SYM(keyword->table[i])); - if (keyword->default_values[j] != Qundef) { + if (!UNDEF_P(keyword->default_values[j])) { rb_ary_push(key, keyword->default_values[j]); } rb_ary_push(keywords, key); diff --git a/lib/irb.rb b/lib/irb.rb index 04009664efd47a..0a856d3929ce98 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -53,6 +53,50 @@ # # :include: ./irb/lc/help-message # +# == Commands +# +# The following commands are available on IRB. +# +# * cwws +# * Show the current workspace. +# * cb, cws, chws +# * Change the current workspace to an object. +# * bindings, workspaces +# * Show workspaces. +# * pushb, pushws +# * Push an object to the workspace stack. +# * popb, popws +# * Pop a workspace from the workspace stack. +# * load +# * Load a Ruby file. +# * require +# * Require a Ruby file. +# * source +# * Loads a given file in the current session. +# * irb +# * Start a child IRB. +# * jobs +# * List of current sessions. +# * fg +# * Switches to the session of the given number. +# * kill +# * Kills the session with the given number. +# * help +# * Enter the mode to look up RI documents. +# * irb_info +# * Show information about IRB. +# * ls +# * Show methods, constants, and variables. +# -g [query] or -G [query] allows you to filter out the output. +# * measure +# * measure enables the mode to measure processing time. measure :off disables it. +# * $, show_source +# * Show the source code of a given method or constant. +# * @, whereami +# * Show the source code around binding.irb again. +# * debug +# * Start the debugger of debug.gem. +# # == Configuration # # IRB reads a personal initialization file when it's invoked. @@ -93,9 +137,9 @@ # # === Autocompletion # -# To enable autocompletion for irb, add the following to your +.irbrc+: +# To disable autocompletion for irb, add the following to your +.irbrc+: # -# require 'irb/completion' +# IRB.conf[:USE_AUTOCOMPLETE] = false # # === History # @@ -434,6 +478,17 @@ def initialize(workspace = nil, input_method = nil) @scanner = RubyLex.new end + # A hook point for `debug` command's TracePoint after :IRB_EXIT as well as its clean-up + def debug_break + # it means the debug command is executed + if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:capture_frames_without_irb) + # after leaving this initial breakpoint, revert the capture_frames patch + DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :capture_frames_without_irb) + # and remove the redundant method + DEBUGGER__.singleton_class.send(:undef_method, :capture_frames_without_irb) + end + end + def run(conf = IRB.conf) conf[:IRB_RC].call(context) if conf[:IRB_RC] conf[:MAIN_CONTEXT] = context @@ -598,11 +653,7 @@ def handle_exception(exc) if exc.backtrace order = nil - if '2.5.0' == RUBY_VERSION - # Exception#full_message doesn't have keyword arguments. - message = exc.full_message # the same of (highlight: true, order: bottom) - order = :bottom - elsif '2.5.1' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' + if RUBY_VERSION < '3.0.0' if STDOUT.tty? message = exc.full_message(order: :bottom) order = :bottom @@ -928,12 +979,13 @@ class Binding # # # See IRB@IRB+Usage for more information. - def irb + def irb(show_code: true) IRB.setup(source_location[0], argv: []) workspace = IRB::WorkSpace.new(self) - STDOUT.print(workspace.code_around_binding) + STDOUT.print(workspace.code_around_binding) if show_code binding_irb = IRB::Irb.new(workspace) binding_irb.context.irb_path = File.expand_path(source_location[0]) binding_irb.run(IRB.conf) + binding_irb.debug_break end end diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb new file mode 100644 index 00000000000000..d43e060c67a2df --- /dev/null +++ b/lib/irb/cmd/debug.rb @@ -0,0 +1,96 @@ +require_relative "nop" + +module IRB + # :stopdoc: + + module ExtendCommand + class Debug < Nop + BINDING_IRB_FRAME_REGEXPS = [ + '', + binding.method(:irb).source_location.first, + ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ } + IRB_DIR = File.expand_path('..', __dir__) + + def execute(*args) + unless binding_irb? + puts "`debug` command is only available when IRB is started with binding.irb" + return + end + + unless setup_debugger + puts <<~MSG + You need to install the debug gem before using this command. + If you use `bundle exec`, please add `gem "debug"` into your Gemfile. + MSG + return + end + + # To make debugger commands like `next` or `continue` work without asking + # the user to quit IRB after that, we need to exit IRB first and then hit + # a TracePoint on #debug_break. + file, lineno = IRB::Irb.instance_method(:debug_break).source_location + DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, oneshot: true, hook_call: false) + # exit current Irb#run call + throw :IRB_EXIT + end + + private + + def binding_irb? + caller.any? do |frame| + BINDING_IRB_FRAME_REGEXPS.any? do |regexp| + frame.match?(regexp) + end + end + end + + def setup_debugger + unless defined?(DEBUGGER__::SESSION) + begin + require "debug/session" + rescue LoadError # debug.gem is not written in Gemfile + return false unless load_bundled_debug_gem + end + DEBUGGER__.start(nonstop: true) + end + + unless DEBUGGER__.respond_to?(:capture_frames_without_irb) + DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames) + + def DEBUGGER__.capture_frames(*args) + frames = capture_frames_without_irb(*args) + frames.reject! do |frame| + frame.realpath&.start_with?(IRB_DIR) || frame.path == "" + end + frames + end + end + + true + end + + # This is used when debug.gem is not written in Gemfile. Even if it's not + # installed by `bundle install`, debug.gem is installed by default because + # it's a bundled gem. This method tries to activate and load that. + def load_bundled_debug_gem + # Discover latest debug.gem under GEM_PATH + debug_gem = Gem.paths.path.map { |path| Dir.glob("#{path}/gems/debug-*") }.flatten.select do |path| + File.basename(path).match?(/\Adebug-\d+\.\d+\.\d+\z/) + end.sort_by do |path| + Gem::Version.new(File.basename(path).delete_prefix('debug-')) + end.last + return false unless debug_gem + + # Attempt to forcibly load the bundled gem + $LOAD_PATH << "#{debug_gem}/lib" + begin + require "debug/session" + puts "Loaded #{File.basename(debug_gem)}" + true + rescue LoadError + false + end + end + end + end +end diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index b41d5de35e3119..34640e17f974c3 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -361,6 +361,7 @@ def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace to_ignore = ignored_modules ObjectSpace.each_object(Module){|m| next if (to_ignore.include?(m) rescue true) + next unless m.respond_to?(:instance_methods) # JRuby has modules that represent java packages. They don't include many common ruby methods candidates.concat m.instance_methods(false).collect{|x| x.to_s} } candidates.sort! diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 72c74f081dca0e..c5d98772b89187 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -49,10 +49,13 @@ def initialize(irb, workspace = nil, input_method = nil) if IRB.conf.has_key?(:USE_MULTILINE) @use_multiline = IRB.conf[:USE_MULTILINE] elsif IRB.conf.has_key?(:USE_RELINE) # backward compatibility + warn <<~MSG.strip + USE_RELINE is deprecated, please use USE_MULTILINE instead. + MSG @use_multiline = IRB.conf[:USE_RELINE] elsif IRB.conf.has_key?(:USE_REIDLINE) warn <<~MSG.strip - USE_REIDLINE is deprecated, please use USE_RELINE instead. + USE_REIDLINE is deprecated, please use USE_MULTILINE instead. MSG @use_multiline = IRB.conf[:USE_REIDLINE] else diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb index 2135184dba37a9..a74b89f8c33978 100644 --- a/lib/irb/ext/save-history.rb +++ b/lib/irb/ext/save-history.rb @@ -73,7 +73,7 @@ def load_history open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f| f.each { |l| l = l.chomp - if self.class == ReidlineInputMethod and history.last&.end_with?("\\") + if self.class == RelineInputMethod and history.last&.end_with?("\\") history.last.delete_suffix!("\\") history.last << "\n" << l else diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index acc23c9920f1ca..94fd9c8bb4fab6 100644 --- a/lib/irb/extend-command.rb +++ b/lib/irb/extend-command.rb @@ -116,6 +116,10 @@ def irb_context [:kill, OVERRIDE_PRIVATE_ONLY], ], + [ + :irb_debug, :Debug, "cmd/debug", + [:debug, NO_OVERRIDE], + ], [ :irb_help, :Help, "cmd/help", [:help, NO_OVERRIDE], @@ -161,26 +165,7 @@ def self.load_command(command) nil end - # Installs the default irb commands: - # - # +irb_current_working_workspace+:: Context#main - # +irb_change_workspace+:: Context#change_workspace - # +irb_workspaces+:: Context#workspaces - # +irb_push_workspace+:: Context#push_workspace - # +irb_pop_workspace+:: Context#pop_workspace - # +irb_load+:: #irb_load - # +irb_require+:: #irb_require - # +irb_source+:: IrbLoader#source_file - # +irb+:: IRB.irb - # +irb_jobs+:: JobManager - # +irb_fg+:: JobManager#switch - # +irb_kill+:: JobManager#kill - # +irb_help+:: IRB@Command+line+options - # +irb_info+:: #inspect - # +irb_ls+:: Output#dump - # +irb_measure+:: IRB::unset_measure_callback - # +irb_show_source+:: #find_source, #show_source - # +irb_whereami+:: Workspace#code_around_binding + # Installs the default irb commands. def self.install_extend_commands for args in @EXTEND_COMMANDS def_extend_command(*args) diff --git a/lib/irb/init.rb b/lib/irb/init.rb index 8c9d473b74d84c..831d7d811ae9c9 100644 --- a/lib/irb/init.rb +++ b/lib/irb/init.rb @@ -260,8 +260,20 @@ def IRB.parse_opts(argv: ::ARGV) when "--nosingleline", "--noreadline" @CONF[:USE_SINGLELINE] = false when "--multiline", "--reidline" + if opt == "--reidline" + warn <<~MSG.strip + --reidline is deprecated, please use --multiline instead. + MSG + end + @CONF[:USE_MULTILINE] = true when "--nomultiline", "--noreidline" + if opt == "--noreidline" + warn <<~MSG.strip + --noreidline is deprecated, please use --nomultiline instead. + MSG + end + @CONF[:USE_MULTILINE] = false when /^--extra-doc-dir(?:=(.+))?/ opt = $1 || argv.shift diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index 5f13971a4350be..94805731956b50 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -461,7 +461,7 @@ def encoding # For debug message def inspect config = Reline::Config.new - str = "ReidlineInputMethod with Reline #{Reline::VERSION}" + str = "RelineInputMethod with Reline #{Reline::VERSION}" if config.respond_to?(:inputrc_path) inputrc_path = File.expand_path(config.inputrc_path) else diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec index d44393804328f4..c3e8a4dc585cf4 100644 --- a/lib/irb/irb.gemspec +++ b/lib/irb/irb.gemspec @@ -34,7 +34,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.required_ruby_version = Gem::Requirement.new(">= 2.5") + spec.required_ruby_version = Gem::Requirement.new(">= 2.6") spec.add_dependency "reline", ">= 0.3.0" end diff --git a/lib/irb/version.rb b/lib/irb/version.rb index 38fe36728ba497..2bb3aa2c99b3cb 100644 --- a/lib/irb/version.rb +++ b/lib/irb/version.rb @@ -11,7 +11,7 @@ # module IRB # :nodoc: - VERSION = "1.4.2" + VERSION = "1.4.3" @RELEASE_VERSION = VERSION - @LAST_UPDATE_DATE = "2022-10-03" + @LAST_UPDATE_DATE = "2022-11-17" end diff --git a/lib/mjit/c_pointer.rb b/lib/mjit/c_pointer.rb index 7f64a8ac8f4906..aadf80e804e86d 100644 --- a/lib/mjit/c_pointer.rb +++ b/lib/mjit/c_pointer.rb @@ -282,12 +282,16 @@ def initialize(addr, width, offset) # Dereference def * - if @width != 1 - raise NotImplementedError.new("Non-1 width is not implemented yet") - end byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first - bit = 1 & (byte >> @offset) - bit == 1 + if @width == 1 + bit = (1 & (byte >> @offset)) + bit == 1 + elsif @width <= 8 && @offset == 0 + bitmask = @width.times.map { |i| 1 << i }.sum + byte & bitmask + else + raise NotImplementedError.new("not-implemented bit field access: width=#{@width} offset=#{@offset}") + end end # @param width [Integer] diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 3ccdc1c925d62b..806e7f55c49c03 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -1,6 +1,4 @@ module RubyVM::MJIT - ROBJECT_EMBED_LEN_MAX = C.ROBJECT_EMBED_LEN_MAX - UNSUPPORTED_INSNS = [ :defineclass, # low priority ] @@ -45,9 +43,9 @@ def compile(f, iseq, funcname, id) C.fprintf(f, "\n} // end of #{funcname}\n") return success - rescue => e # Should rb_rescue be called in C? + rescue Exception => e # should we use rb_rescue in C instead? if C.mjit_opts.warnings || C.mjit_opts.verbose > 0 - $stderr.puts e.full_message + $stderr.puts "MJIT error: #{e.full_message}" end return false end diff --git a/lib/net/http.rb b/lib/net/http.rb index 16137cebf22d12..f07586e656031e 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -32,110 +32,237 @@ class HTTPBadResponse < StandardError; end class HTTPHeaderSyntaxError < StandardError; end # :startdoc: - # == An HTTP client API for Ruby. + # \Class \Net::HTTP provides a rich library that implements the client + # in a client-server model that uses the \HTTP request-response protocol. + # For information about \HTTP, see + # + # - {Hypertext Transfer Protocol}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol]. + # - {Technical overview}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technical_overview]. + # + # Note: If you are performing only a few GET requests, consider using + # {OpenURI}[rdoc-ref:OpenURI]; + # otherwise, read on. + # + # == Synopsis + # + # If you are already familiar with \HTTP, this synopsis may be helpful. + # + # {Session}[rdoc-ref:Net::HTTP@Sessions] with multiple requests for + # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]: + # + # Net::HTTP.start(hostname) do |http| + # # Session started automatically before block execution. + # http.get(path_or_uri, headers = {}) + # http.head(path_or_uri, headers = {}) + # http.post(path_or_uri, data, headers = {}) # Can also have a block. + # http.put(path_or_uri, data, headers = {}) + # http.delete(path_or_uri, headers = {Depth: 'Infinity'}) + # http.options(path_or_uri, headers = {}) + # http.trace(path_or_uri, headers = {}) + # http.patch(path_or_uri, data, headers = {}) # Can also have a block. + # # Session finished automatically at block exit. + # end # - # Net::HTTP provides a rich library which can be used to build HTTP - # user-agents. For more details about HTTP see - # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt). + # {Session}[rdoc-ref:Net::HTTP@Sessions] with multiple requests for + # {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]: + # + # Net::HTTP.start(hostname) do |http| + # # Session started automatically before block execution. + # http.copy(path_or_uri, headers = {}) + # http.lock(path_or_uri, body, headers = {}) + # http.mkcol(path_or_uri, body = nil, headers = {}) + # http.move(path_or_uri, headers = {}) + # http.propfind(path_or_uri, body = nil, headers = {'Depth' => '0'}) + # http.proppatch(path_or_uri, body, headers = {}) + # http.unlock(path_or_uri, body, headers = {}) + # # Session finished automatically at block exit. + # end # - # Net::HTTP is designed to work closely with URI. URI::HTTP#host, - # URI::HTTP#port and URI::HTTP#request_uri are designed to work with - # Net::HTTP. + # Each of the following methods automatically starts and finishes + # a {session}[rdoc-ref:Net::HTTP@Sessions] that sends a single request: # - # If you are only performing a few GET requests you should try OpenURI. + # # Return string response body. + # Net::HTTP.get(hostname, path, port = 80) + # Net::HTTP.get(uri, headers = {}, port = 80) # - # == Simple Examples + # # Write string response body to $stdout. + # Net::HTTP.get_print(hostname, path_or_uri, port = 80) + # Net::HTTP.get_print(uri, headers = {}, port = 80) # - # All examples assume you have loaded Net::HTTP with: + # # Return response as Net::HTTPResponse object. + # Net::HTTP.get_response(hostname, path_or_uri, port = 80) + # Net::HTTP.get_response(uri, headers = {}, port = 80) # - # require 'net/http' + # Net::HTTP.post(uri, data, headers = {}) + # Net::HTTP.post_form(uri, params) # - # This will also require 'uri' so you don't need to require it separately. + # == About the Examples # - # The Net::HTTP methods in the following section do not persist - # connections. They are not recommended if you are performing many HTTP - # requests. + # :include: doc/net-http/examples.rdoc # - # === GET + # == URIs # - # Net::HTTP.get('example.com', '/index.html') # => String + # On the internet, a URI + # ({Universal Resource Identifier}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier]) + # is a string that identifies a particular resource. + # It consists of some or all of: scheme, hostname, path, query, and fragment; + # see {URI syntax}[https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax]. # - # === GET by URI + # A Ruby {URI::Generic}[rdoc-ref:URI::Generic] object + # represents an internet URI. + # It provides, among others, methods + # +scheme+, +hostname+, +path+, +query+, and +fragment+. # - # uri = URI('http://example.com/index.html?count=10') - # Net::HTTP.get(uri) # => String + # === Schemes # - # === GET with Dynamic Parameters + # An internet \URI has + # a {scheme}[https://en.wikipedia.org/wiki/List_of_URI_schemes]. # - # uri = URI('http://example.com/index.html') - # params = { :limit => 10, :page => 3 } - # uri.query = URI.encode_www_form(params) + # The two schemes supported in \Net::HTTP are 'https' and 'http': # - # res = Net::HTTP.get_response(uri) - # puts res.body if res.is_a?(Net::HTTPSuccess) + # uri.scheme # => "https" + # URI('http://example.com').scheme # => "http" # - # === POST + # === Hostnames # - # uri = URI('http://www.example.com/search.cgi') - # res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50') - # puts res.body + # A hostname identifies a server (host) to which requests may be sent: # - # === POST with Multiple Values + # hostname = uri.hostname # => "jsonplaceholder.typicode.com" + # Net::HTTP.start(hostname) do |http| + # # Some HTTP stuff. + # end # - # uri = URI('http://www.example.com/search.cgi') - # res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50') - # puts res.body + # === Paths # - # == How to use Net::HTTP + # A host-specific path identifies a resource on the host: # - # The following example code can be used as the basis of an HTTP user-agent - # which can perform a variety of request types using persistent - # connections. + # _uri = uri.dup + # _uri.path = '/todos/1' + # hostname = _uri.hostname + # path = _uri.path + # Net::HTTP.get(hostname, path) # - # uri = URI('http://example.com/some_path?query=string') + # === Queries # - # Net::HTTP.start(uri.host, uri.port) do |http| - # request = Net::HTTP::Get.new uri + # A host-specific query adds name/value pairs to the URI: # - # response = http.request request # Net::HTTPResponse object - # end + # _uri = uri.dup + # params = {userId: 1, completed: false} + # _uri.query = URI.encode_www_form(params) + # _uri # => # + # Net::HTTP.get(_uri) # - # Net::HTTP::start immediately creates a connection to an HTTP server which - # is kept open for the duration of the block. The connection will remain - # open for multiple requests in the block if the server indicates it - # supports persistent connections. + # === Fragments # - # If you wish to re-use a connection across multiple HTTP requests without - # automatically closing it you can use ::new and then call #start and - # #finish manually. + # A {URI fragment}[https://en.wikipedia.org/wiki/URI_fragment] has no effect + # in \Net::HTTP; + # the same data is returned, regardless of whether a fragment is included. # - # The request types Net::HTTP supports are listed below in the section "HTTP - # Request Classes". + # == Request Headers # - # For all the Net::HTTP request objects and shortcut request methods you may - # supply either a String for the request path or a URI from which Net::HTTP - # will extract the request path. + # Request headers may be used to pass additional information to the host, + # similar to arguments passed in a method call; + # each header is a name/value pair. # - # === Response Data + # Each of the \Net::HTTP methods that sends a request to the host + # has optional argument +headers+, + # where the headers are expressed as a hash of field-name/value pairs: # - # uri = URI('http://example.com/index.html') - # res = Net::HTTP.get_response(uri) + # headers = {Accept: 'application/json', Connection: 'Keep-Alive'} + # Net::HTTP.get(uri, headers) # - # # Headers - # res['Set-Cookie'] # => String - # res.get_fields('set-cookie') # => Array - # res.to_hash['set-cookie'] # => Array - # puts "Headers: #{res.to_hash.inspect}" + # See lists of both standard request fields and common request fields at + # {Request Fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields]. + # A host may also accept other custom fields. # - # # Status - # puts res.code # => '200' - # puts res.message # => 'OK' - # puts res.class.name # => 'HTTPOK' + # == Sessions # - # # Body - # puts res.body + # A _session_ is a connection between a server (host) and a client that: + # + # - Is begun by instance method Net::HTTP#start. + # - May contain any number of requests. + # - Is ended by instance method Net::HTTP#finish. + # + # See example sessions at the {Synopsis}[rdoc-ref:Net::HTTP@Synopsis]. + # + # === Session Using \Net::HTTP.start + # + # If you have many requests to make to a single host (and port), + # consider using singleton method Net::HTTP.start with a block; + # the method handles the session automatically by: + # + # - Calling #start before block execution. + # - Executing the block. + # - Calling #finish after block execution. + # + # In the block, you can use these instance methods, + # each of which that sends a single request: + # + # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]: + # + # - #get, #request_get: GET. + # - #head, #request_head: HEAD. + # - #post, #request_post: POST. + # - #delete: DELETE. + # - #options: OPTIONS. + # - #trace: TRACE. + # - #patch: PATCH. + # + # - {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]: + # + # - #copy: COPY. + # - #lock: LOCK. + # - #mkcol: MKCOL. + # - #move: MOVE. + # - #propfind: PROPFIND. + # - #proppatch: PROPPATCH. + # - #unlock: UNLOCK. # - # === Following Redirection + # === Session Using \Net::HTTP.start and \Net::HTTP.finish + # + # You can manage a session manually using methods #start and #finish: + # + # http = Net::HTTP.new(hostname) + # http.start + # http.get('/todos/1') + # http.get('/todos/2') + # http.delete('/posts/1') + # http.finish # Needed to free resources. + # + # === Single-Request Session + # + # Certain convenience methods automatically handle a session by: + # + # - Creating an \HTTP object + # - Starting a session. + # - Sending a single request. + # - Finishing the session. + # - Destroying the object. + # + # Such methods that send GET requests: + # + # - ::get: Returns the string response body. + # - ::get_print: Writes the string response body to $stdout. + # - ::get_response: Returns a Net::HTTPResponse object. + # + # Such methods that send POST requests: + # + # - ::post: Posts data to the host. + # - ::post_form: Posts form data to the host. + # + # == \HTTP Requests and Responses + # + # Many of the methods above are convenience methods, + # each of which sends a request and returns a string + # without directly using \Net::HTTPRequest and \Net::HTTPResponse objects. + # + # You can, however, directly create a request object, send the request, + # and retrieve the response object; see: + # + # - Net::HTTPRequest. + # - Net::HTTPResponse. + # + # == Following Redirection # # Each Net::HTTPResponse object belongs to a class for its response code. # @@ -167,56 +294,7 @@ class HTTPHeaderSyntaxError < StandardError; end # # print fetch('http://www.ruby-lang.org') # - # === POST - # - # A POST can be made using the Net::HTTP::Post request class. This example - # creates a URL encoded POST body: - # - # uri = URI('http://www.example.com/todo.cgi') - # req = Net::HTTP::Post.new(uri) - # req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31') - # - # res = Net::HTTP.start(uri.hostname, uri.port) do |http| - # http.request(req) - # end - # - # case res - # when Net::HTTPSuccess, Net::HTTPRedirection - # # OK - # else - # res.value - # end - # - # To send multipart/form-data use Net::HTTPHeader#set_form: - # - # req = Net::HTTP::Post.new(uri) - # req.set_form([['upload', File.open('foo.bar')]], 'multipart/form-data') - # - # Other requests that can contain a body such as PUT can be created in the - # same way using the corresponding request class (Net::HTTP::Put). - # - # === Setting Headers - # - # The following example performs a conditional GET using the - # If-Modified-Since header. If the files has not been modified since the - # time in the header a Not Modified response will be returned. See RFC 2616 - # section 9.3 for further details. - # - # uri = URI('http://example.com/cached_response') - # file = File.stat 'cached_response' - # - # req = Net::HTTP::Get.new(uri) - # req['If-Modified-Since'] = file.mtime.rfc2822 - # - # res = Net::HTTP.start(uri.hostname, uri.port) {|http| - # http.request(req) - # } - # - # open 'cached_response', 'w' do |io| - # io.write res.body - # end if res.is_a?(Net::HTTPSuccess) - # - # === Basic Authentication + # == Basic Authentication # # Basic authentication is performed according to # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt). @@ -231,7 +309,7 @@ class HTTPHeaderSyntaxError < StandardError; end # } # puts res.body # - # === Streaming Response Bodies + # == Streaming Response Bodies # # By default Net::HTTP reads an entire response into memory. If you are # handling large files or wish to implement a progress bar you can instead @@ -251,7 +329,7 @@ class HTTPHeaderSyntaxError < StandardError; end # end # end # - # === HTTPS + # == HTTPS # # HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=. # @@ -272,7 +350,7 @@ class HTTPHeaderSyntaxError < StandardError; end # In previous versions of Ruby you would need to require 'net/https' to use # HTTPS. This is no longer true. # - # === Proxies + # == Proxies # # Net::HTTP will automatically create a proxy from the +http_proxy+ # environment variable if it is present. To disable use of +http_proxy+, @@ -290,7 +368,7 @@ class HTTPHeaderSyntaxError < StandardError; end # See Net::HTTP.new for further details and examples such as proxies that # require a username and password. # - # === Compression + # == Compression # # Net::HTTP automatically adds Accept-Encoding for compression of response # bodies and automatically decompresses gzip and deflate responses unless a @@ -298,114 +376,6 @@ class HTTPHeaderSyntaxError < StandardError; end # # Compression can be disabled through the Accept-Encoding: identity header. # - # == \HTTP Request Classes - # - # Here is the hierarchy of \HTTP request classes - # - # - Net::HTTPRequest - # - # - Net::HTTP::Get - # - Net::HTTP::Head - # - Net::HTTP::Post - # - Net::HTTP::Patch - # - Net::HTTP::Put - # - Net::HTTP::Proppatch - # - Net::HTTP::Lock - # - Net::HTTP::Unlock - # - Net::HTTP::Options - # - Net::HTTP::Propfind - # - Net::HTTP::Delete - # - Net::HTTP::Move - # - Net::HTTP::Copy - # - Net::HTTP::Mkcol - # - Net::HTTP::Trace - # - # == \HTTP Response Classes - # - # Here is the \HTTP response class hierarchy (with status codes): - # - # - Net::HTTPResponse: - # - # - Net::HTTPUnknownResponse (for unhandled \HTTP extensions). - # - # - Net::HTTPInformation: - # - # - Net::HTTPContinue (100) - # - Net::HTTPSwitchProtocol (101) - # - Net::HTTPProcessing (102) - # - Net::HTTPEarlyHints (103) - # - # - Net::HTTPSuccess: - # - # - Net::HTTPOK (200) - # - Net::HTTPCreated (201) - # - Net::HTTPAccepted (202) - # - Net::HTTPNonAuthoritativeInformation (203) - # - Net::HTTPNoContent (204) - # - Net::HTTPResetContent (205) - # - Net::HTTPPartialContent (206) - # - Net::HTTPMultiStatus (207) - # - Net::HTTPAlreadyReported (208) - # - Net::HTTPIMUsed (226) - # - # - Net::HTTPRedirection: - # - # - Net::HTTPMultipleChoices (300) - # - Net::HTTPMovedPermanently (301) - # - Net::HTTPFound (302) - # - Net::HTTPSeeOther (303) - # - Net::HTTPNotModified (304) - # - Net::HTTPUseProxy (305) - # - Net::HTTPTemporaryRedirect (307) - # - Net::HTTPPermanentRedirect (308) - # - # - Net::HTTPClientError: - # - # - Net::HTTPBadRequest (400) - # - Net::HTTPUnauthorized (401) - # - Net::HTTPPaymentRequired (402) - # - Net::HTTPForbidden (403) - # - Net::HTTPNotFound (404) - # - Net::HTTPMethodNotAllowed (405) - # - Net::HTTPNotAcceptable (406) - # - Net::HTTPProxyAuthenticationRequired (407) - # - Net::HTTPRequestTimeOut (408) - # - Net::HTTPConflict (409) - # - Net::HTTPGone (410) - # - Net::HTTPLengthRequired (411) - # - Net::HTTPPreconditionFailed (412) - # - Net::HTTPRequestEntityTooLarge (413) - # - Net::HTTPRequestURITooLong (414) - # - Net::HTTPUnsupportedMediaType (415) - # - Net::HTTPRequestedRangeNotSatisfiable (416) - # - Net::HTTPExpectationFailed (417) - # - Net::HTTPMisdirectedRequest (421) - # - Net::HTTPUnprocessableEntity (422) - # - Net::HTTPLocked (423) - # - Net::HTTPFailedDependency (424) - # - Net::HTTPUpgradeRequired (426) - # - Net::HTTPPreconditionRequired (428) - # - Net::HTTPTooManyRequests (429) - # - Net::HTTPRequestHeaderFieldsTooLarge (431) - # - Net::HTTPUnavailableForLegalReasons (451) - # - # - Net::HTTPServerError: - # - # - Net::HTTPInternalServerError (500) - # - Net::HTTPNotImplemented (501) - # - Net::HTTPBadGateway (502) - # - Net::HTTPServiceUnavailable (503) - # - Net::HTTPGatewayTimeOut (504) - # - Net::HTTPVersionNotSupported (505) - # - Net::HTTPVariantAlsoNegotiates (506) - # - Net::HTTPInsufficientStorage (507) - # - Net::HTTPLoopDetected (508) - # - Net::HTTPNotExtended (510) - # - Net::HTTPNetworkAuthenticationRequired (511) - # - # There is also the Net::HTTPBadResponse exception which is raised when - # there is a protocol error. - # class HTTP < Protocol # :stopdoc: @@ -420,18 +390,17 @@ class HTTP < Protocol end # :startdoc: - # Turns on net/http 1.2 (Ruby 1.8) features. - # Defaults to ON in Ruby 1.8 or later. + # Returns +true+; retained for compatibility. def HTTP.version_1_2 true end - # Returns true if net/http is in version 1.2 mode. - # Defaults to true. + # Returns +true+; retained for compatibility. def HTTP.version_1_2? true end + # Returns +false+; retained for compatibility. def HTTP.version_1_1? #:nodoc: false end @@ -441,25 +410,12 @@ class << HTTP alias is_version_1_2? version_1_2? #:nodoc: end + # :call-seq: + # Net::HTTP.get_print(hostname, path, port = 80) -> nil + # Net::HTTP:get_print(uri, headers = {}, port = uri.port) -> nil # - # short cut methods - # - - # - # Gets the body text from the target and outputs it to $stdout. The - # target can either be specified as - # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so: - # - # Net::HTTP.get_print URI('http://www.example.com/index.html') - # - # or: - # - # Net::HTTP.get_print 'www.example.com', '/index.html' - # - # you can also specify request headers: - # - # Net::HTTP.get_print URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' } - # + # Like Net::HTTP.get, but writes the returned body to $stdout; + # returns +nil+. def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil) get_response(uri_or_host, path_or_headers, port) {|res| res.read_body do |chunk| @@ -469,40 +425,43 @@ def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil) nil end - # Sends a GET request to the target and returns the HTTP response - # as a string. The target can either be specified as - # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so: + # :call-seq: + # Net::HTTP.get(hostname, path, port = 80) -> body + # Net::HTTP:get(uri, headers = {}, port = uri.port) -> body # - # print Net::HTTP.get(URI('http://www.example.com/index.html')) + # Sends a GET request and returns the \HTTP response body as a string. # - # or: + # With string arguments +hostname+ and +path+: # - # print Net::HTTP.get('www.example.com', '/index.html') + # hostname = 'jsonplaceholder.typicode.com' + # path = '/todos/1' + # puts Net::HTTP.get(hostname, path) # - # you can also specify request headers: + # Output: # - # Net::HTTP.get(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }) + # { + # "userId": 1, + # "id": 1, + # "title": "delectus aut autem", + # "completed": false + # } + # + # With URI object +uri+ and optional hash argument +headers+: + # + # uri = URI('https://jsonplaceholder.typicode.com/todos/1') + # headers = {'Content-type' => 'application/json; charset=UTF-8'} + # Net::HTTP.get(uri, headers) # def HTTP.get(uri_or_host, path_or_headers = nil, port = nil) get_response(uri_or_host, path_or_headers, port).body end - # Sends a GET request to the target and returns the HTTP response - # as a Net::HTTPResponse object. The target can either be specified as - # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so: - # - # res = Net::HTTP.get_response(URI('http://www.example.com/index.html')) - # print res.body - # - # or: - # - # res = Net::HTTP.get_response('www.example.com', '/index.html') - # print res.body - # - # you can also specify request headers: - # - # Net::HTTP.get_response(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }) + # :call-seq: + # Net::HTTP.get_response(hostname, path, port = 80) -> http_response + # Net::HTTP:get_response(uri, headers = {}, port = uri.port) -> http_response # + # Like Net::HTTP.get, but returns an Net::HTTPResponse object + # instead of the body string. def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block) if path_or_headers && !path_or_headers.is_a?(Hash) host = uri_or_host @@ -589,35 +548,80 @@ def HTTP.socket_type #:nodoc: obsolete end # :call-seq: - # HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block) - # HTTP.start(address, port=nil, p_addr=:ENV, p_port=nil, p_user=nil, p_pass=nil, opt, &block) - # - # Creates a new Net::HTTP object, then additionally opens the TCP - # connection and HTTP session. - # - # Arguments are the following: - # _address_ :: hostname or IP address of the server - # _port_ :: port of the server - # _p_addr_ :: address of proxy - # _p_port_ :: port of proxy - # _p_user_ :: user of proxy - # _p_pass_ :: pass of proxy - # _opt_ :: optional hash - # - # _opt_ sets following values by its accessor. - # The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers, keep_alive_timeout, - # close_on_empty_response, key, open_timeout, read_timeout, write_timeout, ssl_timeout, - # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode. - # If you set :use_ssl as true, you can use https and default value of - # verify_mode is set as OpenSSL::SSL::VERIFY_PEER. - # - # If the optional block is given, the newly - # created Net::HTTP object is passed to it and closed when the - # block finishes. In this case, the return value of this method - # is the return value of the block. If no block is given, the - # return value of this method is the newly created Net::HTTP object - # itself, and the caller is responsible for closing it upon completion - # using the finish() method. + # HTTP.start(address, port, p_addr, p_port, p_user, p_pass) {|http| ... } + # HTTP.start(address, port=nil, p_addr=:ENV, p_port=nil, p_user=nil, p_pass=nil, opt) {|http| ... } + # Creates a new \Net::HTTP object, + # opens a TCP connection and \HTTP session. + # + # Argument +address+ is the hostname or IP address of the server. + # + # With a block given: + # + # - Passes the object to the given block, + # which may make any number of requests to the host. + # - Closes the \HTTP session on block exit. + # - Returns the block's value. + # + # Example: + # + # hostname = 'jsonplaceholder.typicode.com' + # Net::HTTP.start(hostname) do |http| + # puts http.get('/todos/1').body + # puts http.get('/todos/2').body + # end + # + # Output: + # + # { + # "userId": 1, + # "id": 1, + # "title": "delectus aut autem", + # "completed": false + # } + # { + # "userId": 1, + # "id": 2, + # "title": "quis ut nam facilis et officia qui", + # "completed": false + # } + # + # With no block given, returns the \Net::HTTP object; + # the caller should call #finish to close the session. + # + # Other arguments: + # + # - +port+: Server port number. + # - +p_addr+: Proxy address. + # - +p_port+: Proxy port. + # - +p_user+: Proxy user name. + # - +p_pass+: Proxy password. + # - +opts+: Optional options hash. + # + # The options hash +opts+ sets certain values, + # where each key is a method or accessor to be called, + # and its value is the value to be set. + # + # The keys may include: + # + # - #ca_file + # - #ca_path + # - #cert + # - #cert_store + # - #ciphers + # - #close_on_empty_response + # - +ipaddr+ (calls #ipaddr=) + # - #keep_alive_timeout + # - #key + # - #open_timeout + # - #read_timeout + # - #ssl_timeout + # - #ssl_version + # - +use_ssl+ (calls #use_ssl=) + # - #verify_callback + # - #verify_depth + # - #verify_mode + # - #write_timeout + # def HTTP.start(address, *arg, &block) # :yield: +http+ arg.pop if opt = Hash.try_convert(arg[-1]) port, p_addr, p_port, p_user, p_pass = *arg diff --git a/lib/net/http/request.rb b/lib/net/http/request.rb index 1e86f3e4b47365..279316e0b9e332 100644 --- a/lib/net/http/request.rb +++ b/lib/net/http/request.rb @@ -1,8 +1,26 @@ # frozen_string_literal: false -# HTTP request class. -# This class wraps together the request header and the request path. -# You cannot use this class directly. Instead, you should use one of its -# subclasses: Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Head. + +# This class is the base class for \Net::HTTP request classes; +# it wraps together the request path and the request headers. +# +# The class should not be used directly; +# instead you should use its subclasses: +# +# - \Net::HTTP::Get +# - \Net::HTTP::Head +# - \Net::HTTP::Post +# - \Net::HTTP::Delete +# - \Net::HTTP::Options +# - \Net::HTTP::Trace +# - \Net::HTTP::Patch +# - \Net::HTTP::Put +# - \Net::HTTP::Copy +# - \Net::HTTP::Lock +# - \Net::HTTP::Mkcol +# - \Net::HTTP::Move +# - \Net::HTTP::Propfind +# - \Net::HTTP::Proppatch +# - \Net::HTTP::Unlock # class Net::HTTPRequest < Net::HTTPGenericRequest # Creates an HTTP request object for +path+. diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb index f8b522f1ff596d..83853ffd1942de 100644 --- a/lib/net/http/response.rb +++ b/lib/net/http/response.rb @@ -1,20 +1,136 @@ # frozen_string_literal: false -# HTTP response class. + +# This class is the base class for \Net::HTTP request classes. +# +# == About the Examples +# +# :include: doc/net-http/examples.rdoc +# +# == Returned Responses +# +# \Method Net::HTTP.get_response returns +# an instance of one of the subclasses of \Net::HTTPResponse: +# +# Net::HTTP.get_response(uri) +# # => # +# Net::HTTP.get_response(hostname, '/nosuch') +# # => # +# +# As does method Net::HTTP#request: +# +# req = Net::HTTP::Get.new(uri) +# Net::HTTP.start(hostname) do |http| +# http.request(req) +# end # => # +# +# \Class \Net::HTTPResponse includes module Net::HTTPHeader, +# which provides access to response header values via (among others): +# +# - \Hash-like method []. +# - Specific reader methods, such as +content_type+. +# +# Examples: +# +# res = Net::HTTP.get_response(uri) # => # +# res['Content-Type'] # => "text/html; charset=UTF-8" +# res.content_type # => "text/html" +# +# == Response Subclasses +# +# \Class \Net::HTTPResponse has a subclass for each +# {HTTP status code}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes]. +# You can look up the response class for a given code: +# +# Net::HTTPResponse::CODE_TO_OBJ['200'] # => Net::HTTPOK +# Net::HTTPResponse::CODE_TO_OBJ['400'] # => Net::HTTPBadRequest +# Net::HTTPResponse::CODE_TO_OBJ['404'] # => Net::HTTPNotFound +# +# And you can retrieve the status code for a response object: +# +# Net::HTTP.get_response(uri).code # => "200" +# Net::HTTP.get_response(hostname, '/nosuch').code # => "404" +# +# The response subclasses (indentation shows class hierarchy): +# +# - Net::HTTPUnknownResponse (for unhandled \HTTP extensions). +# +# - Net::HTTPInformation: +# +# - Net::HTTPContinue (100) +# - Net::HTTPSwitchProtocol (101) +# - Net::HTTPProcessing (102) +# - Net::HTTPEarlyHints (103) +# +# - Net::HTTPSuccess: +# +# - Net::HTTPOK (200) +# - Net::HTTPCreated (201) +# - Net::HTTPAccepted (202) +# - Net::HTTPNonAuthoritativeInformation (203) +# - Net::HTTPNoContent (204) +# - Net::HTTPResetContent (205) +# - Net::HTTPPartialContent (206) +# - Net::HTTPMultiStatus (207) +# - Net::HTTPAlreadyReported (208) +# - Net::HTTPIMUsed (226) +# +# - Net::HTTPRedirection: +# +# - Net::HTTPMultipleChoices (300) +# - Net::HTTPMovedPermanently (301) +# - Net::HTTPFound (302) +# - Net::HTTPSeeOther (303) +# - Net::HTTPNotModified (304) +# - Net::HTTPUseProxy (305) +# - Net::HTTPTemporaryRedirect (307) +# - Net::HTTPPermanentRedirect (308) # -# This class wraps together the response header and the response body (the -# entity requested). +# - Net::HTTPClientError: # -# It mixes in the HTTPHeader module, which provides access to response -# header values both via hash-like methods and via individual readers. +# - Net::HTTPBadRequest (400) +# - Net::HTTPUnauthorized (401) +# - Net::HTTPPaymentRequired (402) +# - Net::HTTPForbidden (403) +# - Net::HTTPNotFound (404) +# - Net::HTTPMethodNotAllowed (405) +# - Net::HTTPNotAcceptable (406) +# - Net::HTTPProxyAuthenticationRequired (407) +# - Net::HTTPRequestTimeOut (408) +# - Net::HTTPConflict (409) +# - Net::HTTPGone (410) +# - Net::HTTPLengthRequired (411) +# - Net::HTTPPreconditionFailed (412) +# - Net::HTTPRequestEntityTooLarge (413) +# - Net::HTTPRequestURITooLong (414) +# - Net::HTTPUnsupportedMediaType (415) +# - Net::HTTPRequestedRangeNotSatisfiable (416) +# - Net::HTTPExpectationFailed (417) +# - Net::HTTPMisdirectedRequest (421) +# - Net::HTTPUnprocessableEntity (422) +# - Net::HTTPLocked (423) +# - Net::HTTPFailedDependency (424) +# - Net::HTTPUpgradeRequired (426) +# - Net::HTTPPreconditionRequired (428) +# - Net::HTTPTooManyRequests (429) +# - Net::HTTPRequestHeaderFieldsTooLarge (431) +# - Net::HTTPUnavailableForLegalReasons (451) # -# Note that each possible HTTP response code defines its own -# HTTPResponse subclass. All classes are defined under the Net module. -# Indentation indicates inheritance. For a list of the classes see Net::HTTP. +# - Net::HTTPServerError: # -# Correspondence HTTP code => class is stored in CODE_TO_OBJ -# constant: +# - Net::HTTPInternalServerError (500) +# - Net::HTTPNotImplemented (501) +# - Net::HTTPBadGateway (502) +# - Net::HTTPServiceUnavailable (503) +# - Net::HTTPGatewayTimeOut (504) +# - Net::HTTPVersionNotSupported (505) +# - Net::HTTPVariantAlsoNegotiates (506) +# - Net::HTTPInsufficientStorage (507) +# - Net::HTTPLoopDetected (508) +# - Net::HTTPNotExtended (510) +# - Net::HTTPNetworkAuthenticationRequired (511) # -# Net::HTTPResponse::CODE_TO_OBJ['404'] #=> Net::HTTPNotFound +# There is also the Net::HTTPBadResponse exception which is raised when +# there is a protocol error. # class Net::HTTPResponse class << self diff --git a/lib/racc/racc.gemspec b/lib/racc/racc.gemspec index 7ee706f63d7139..1095c8f47e0383 100644 --- a/lib/racc/racc.gemspec +++ b/lib/racc/racc.gemspec @@ -20,7 +20,7 @@ Racc is a LALR(1) parser generator. DESC s.authors = ["Minero Aoki", "Aaron Patterson"] s.email = [nil, "aaron@tenderlovemaking.com"] - s.homepage = "https://i.loveruby.net/en/projects/racc/" + s.homepage = "https://github.com/ruby/racc" s.licenses = ["Ruby", "BSD-2-Clause"] s.executables = ["racc"] s.files = [ diff --git a/load.c b/load.c index 0e75c1ab838fc7..5900a6af8e0b10 100644 --- a/load.c +++ b/load.c @@ -1028,13 +1028,13 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_ switch (type) { case 0: if (ft) - goto statically_linked; + goto feature_present; ftptr = RSTRING_PTR(tmp); return rb_feature_p(vm, ftptr, 0, FALSE, TRUE, 0); default: if (ft) { - goto statically_linked; + goto feature_present; } /* fall through */ case 1: @@ -1045,7 +1045,7 @@ search_required(rb_vm_t *vm, VALUE fname, volatile VALUE *path, feature_func rb_ } return type ? 's' : 'r'; - statically_linked: + feature_present: if (loading) *path = rb_filesystem_str_new_cstr(loading); return ft; } diff --git a/marshal.c b/marshal.c index d956260d96f0a3..e1612303fe6bd4 100644 --- a/marshal.c +++ b/marshal.c @@ -743,7 +743,7 @@ w_ivar(st_index_t num, VALUE ivobj, VALUE encname, struct dump_call_arg *arg) w_object(Qtrue, arg->arg, limit); num--; } - if (ivobj != Qundef && num) { + if (!UNDEF_P(ivobj) && num) { w_ivar_each(ivobj, num, arg); } } @@ -930,7 +930,7 @@ w_object(VALUE obj, struct dump_arg *arg, int limit) arg->compat_tbl = rb_init_identtable(); } st_insert(arg->compat_tbl, (st_data_t)obj, (st_data_t)real_obj); - if (obj != real_obj && ivobj == Qundef) hasiv = 0; + if (obj != real_obj && UNDEF_P(ivobj)) hasiv = 0; } } if (hasiv) w_byte(TYPE_IVAR, arg); @@ -2251,7 +2251,7 @@ r_object_for(struct load_arg *arg, bool partial, int *ivp, VALUE extmod, int typ break; } - if (v == Qundef) { + if (UNDEF_P(v)) { rb_raise(rb_eArgError, "dump format error (bad link)"); } diff --git a/mjit.c b/mjit.c index d1a9e71f4f1a7e..824287e3290abe 100644 --- a/mjit.c +++ b/mjit.c @@ -1628,7 +1628,7 @@ system_tmpdir(void) // Default permitted number of units with a JIT code kept in memory. #define DEFAULT_MAX_CACHE_SIZE 10000 // A default threshold used to add iseq to JIT. -#define DEFAULT_MIN_CALLS_TO_ADD 10000 +#define DEFAULT_CALL_THRESHOLD 10000 // Start MJIT worker. Return TRUE if worker is successfully started. static bool @@ -1709,8 +1709,8 @@ mjit_setup_options(const char *s, struct mjit_options *mjit_opt) else if (opt_match_arg(s, l, "max-cache")) { mjit_opt->max_cache_size = atoi(s + 1); } - else if (opt_match_arg(s, l, "min-calls")) { - mjit_opt->min_calls = atoi(s + 1); + else if (opt_match_arg(s, l, "call-threshold")) { + mjit_opt->call_threshold = atoi(s + 1); } // --mjit=pause is an undocumented feature for experiments else if (opt_match_noarg(s, l, "pause")) { @@ -1724,15 +1724,15 @@ mjit_setup_options(const char *s, struct mjit_options *mjit_opt) #define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc) const struct ruby_opt_message mjit_option_messages[] = { - M("--mjit-warnings", "", "Enable printing JIT warnings"), - M("--mjit-debug", "", "Enable JIT debugging (very slow), or add cflags if specified"), - M("--mjit-wait", "", "Wait until JIT compilation finishes every time (for testing)"), - M("--mjit-save-temps", "", "Save JIT temporary files in $TMP or /tmp (for testing)"), - M("--mjit-verbose=num", "", "Print JIT logs of level num or less to stderr (default: 0)"), - M("--mjit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: " + M("--mjit-warnings", "", "Enable printing JIT warnings"), + M("--mjit-debug", "", "Enable JIT debugging (very slow), or add cflags if specified"), + M("--mjit-wait", "", "Wait until JIT compilation finishes every time (for testing)"), + M("--mjit-save-temps", "", "Save JIT temporary files in $TMP or /tmp (for testing)"), + M("--mjit-verbose=num", "", "Print JIT logs of level num or less to stderr (default: 0)"), + M("--mjit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: " STRINGIZE(DEFAULT_MAX_CACHE_SIZE) ")"), - M("--mjit-min-calls=num", "", "Number of calls to trigger JIT (for testing, default: " - STRINGIZE(DEFAULT_MIN_CALLS_TO_ADD) ")"), + M("--mjit-call-threshold=num", "", "Number of calls to trigger JIT (for testing, default: " + STRINGIZE(DEFAULT_CALL_THRESHOLD) ")"), {0} }; #undef M @@ -1760,8 +1760,8 @@ mjit_init(const struct mjit_options *opts) mjit_pid = getpid(); // Normalize options - if (mjit_opts.min_calls == 0) - mjit_opts.min_calls = DEFAULT_MIN_CALLS_TO_ADD; + if (mjit_opts.call_threshold == 0) + mjit_opts.call_threshold = DEFAULT_CALL_THRESHOLD; if (mjit_opts.max_cache_size <= 0) mjit_opts.max_cache_size = DEFAULT_MAX_CACHE_SIZE; if (mjit_opts.max_cache_size < MIN_CACHE_SIZE) diff --git a/mjit.h b/mjit.h index 7b5ca54c6fc259..536ac9fa916c8e 100644 --- a/mjit.h +++ b/mjit.h @@ -52,7 +52,7 @@ struct mjit_options { // If true, all ISeqs are synchronously compiled. For testing. bool wait; // Number of calls to trigger JIT compilation. For testing. - unsigned int min_calls; + unsigned int call_threshold; // Force printing info about MJIT work of level VERBOSE or // less. 0=silence, 1=medium, 2=verbose. int verbose; diff --git a/mjit_c.rb b/mjit_c.rb index 322a8f15cfa465..c240a5ec4b9ca8 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -7,10 +7,6 @@ module RubyVM::MJIT # This `class << C` section is for calling C functions. For importing variables # or macros as is, please consider using tool/mjit/bindgen.rb instead. class << C - def ROBJECT_EMBED_LEN_MAX - Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' - end - def cdhash_to_hash(cdhash_addr) Primitive.cdhash_to_hash(cdhash_addr) end @@ -162,10 +158,6 @@ def C.VM_METHOD_TYPE_ISEQ Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) } end - def C.SHAPE_BITS - Primitive.cexpr! %q{ UINT2NUM(SHAPE_BITS) } - end - def C.SHAPE_CAPACITY_CHANGE Primitive.cexpr! %q{ UINT2NUM(SHAPE_CAPACITY_CHANGE) } end @@ -178,6 +170,10 @@ def C.SHAPE_FROZEN Primitive.cexpr! %q{ UINT2NUM(SHAPE_FROZEN) } end + def C.SHAPE_ID_NUM_BITS + Primitive.cexpr! %q{ UINT2NUM(SHAPE_ID_NUM_BITS) } + end + def C.SHAPE_INITIAL_CAPACITY Primitive.cexpr! %q{ UINT2NUM(SHAPE_INITIAL_CAPACITY) } end @@ -330,7 +326,7 @@ def C.mjit_options debug: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug)")], debug_flags: [CType::Pointer.new { CType::Immediate.parse("char") }, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), debug_flags)")], wait: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), wait)")], - min_calls: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), min_calls)")], + call_threshold: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), call_threshold)")], verbose: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), verbose)")], max_cache_size: [CType::Immediate.parse("int"), Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), max_cache_size)")], pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct mjit_options *)NULL)), pause)")], @@ -569,7 +565,7 @@ def C.rb_iseq_t def C.rb_method_definition_struct @rb_method_definition_struct ||= CType::Struct.new( "rb_method_definition_struct", Primitive.cexpr!("SIZEOF(struct rb_method_definition_struct)"), - type: [self.rb_method_type_t, 0], + type: [CType::BitField.new(4, 0), 0], iseq_overload: [CType::BitField.new(1, 4), 4], alias_count: [CType::BitField.new(27, 5), 5], complemented_count: [CType::BitField.new(28, 0), 32], diff --git a/node.h b/node.h index 39427257ddc466..220de3135e136e 100644 --- a/node.h +++ b/node.h @@ -461,6 +461,7 @@ struct rb_args_info { NODE *opt_args; unsigned int no_kwarg: 1; unsigned int ruby2_keywords: 1; + unsigned int forwarding: 1; VALUE imemo; }; diff --git a/numeric.c b/numeric.c index 8607d697942a3a..b191b25d79e041 100644 --- a/numeric.c +++ b/numeric.c @@ -449,7 +449,7 @@ static int do_coerce(VALUE *x, VALUE *y, int err) { VALUE ary = rb_check_funcall(*y, id_coerce, 1, x); - if (ary == Qundef) { + if (UNDEF_P(ary)) { if (err) { coerce_failed(*x, *y); } @@ -1713,7 +1713,7 @@ flo_cmp(VALUE x, VALUE y) b = RFLOAT_VALUE(y); } else { - if (isinf(a) && (i = rb_check_funcall(y, rb_intern("infinite?"), 0, 0)) != Qundef) { + if (isinf(a) && !UNDEF_P(i = rb_check_funcall(y, rb_intern("infinite?"), 0, 0))) { if (RTEST(i)) { int j = rb_cmpint(i, x, y); j = (a > 0.0) ? (j > 0 ? 0 : +1) : (j < 0 ? 0 : -1); @@ -2858,7 +2858,7 @@ num_step_negative_p(VALUE num) } r = rb_check_funcall(num, '>', 1, &zero); - if (r == Qundef) { + if (UNDEF_P(r)) { coerce_failed(num, INT2FIX(0)); } return !RTEST(r); @@ -2876,11 +2876,11 @@ num_step_extract_args(int argc, const VALUE *argv, VALUE *to, VALUE *step, VALUE keys[0] = id_to; keys[1] = id_by; rb_get_kwargs(hash, keys, 0, 2, values); - if (values[0] != Qundef) { + if (!UNDEF_P(values[0])) { if (argc > 0) rb_raise(rb_eArgError, "to is given twice"); *to = values[0]; } - if (values[1] != Qundef) { + if (!UNDEF_P(values[1])) { if (argc > 1) rb_raise(rb_eArgError, "step is given twice"); *by = values[1]; } @@ -2893,7 +2893,7 @@ static int num_step_check_fix_args(int argc, VALUE *to, VALUE *step, VALUE by, int fix_nil, int allow_zero_step) { int desc; - if (by != Qundef) { + if (!UNDEF_P(by)) { *step = by; } else { @@ -3041,7 +3041,7 @@ num_step(int argc, VALUE *argv, VALUE from) VALUE by = Qundef; num_step_extract_args(argc, argv, &to, &step, &by); - if (by != Qundef) { + if (!UNDEF_P(by)) { step = by; } if (NIL_P(step)) { @@ -4930,7 +4930,7 @@ rb_num_coerce_bit(VALUE x, VALUE y, ID func) do_coerce(&args[1], &args[2], TRUE); ret = rb_exec_recursive_paired(num_funcall_bit_1, args[2], args[1], (VALUE)args); - if (ret == Qundef) { + if (UNDEF_P(ret)) { /* show the original object, not coerced object */ coerce_failed(x, y); } diff --git a/object.c b/object.c index 6251dacae22dd9..c28e96ed362c76 100644 --- a/object.c +++ b/object.c @@ -129,7 +129,7 @@ rb_equal(VALUE obj1, VALUE obj2) if (obj1 == obj2) return Qtrue; result = rb_equal_opt(obj1, obj2); - if (result == Qundef) { + if (UNDEF_P(result)) { result = rb_funcall(obj1, id_eq, 1, obj2); } return RBOOL(RTEST(result)); @@ -142,7 +142,7 @@ rb_eql(VALUE obj1, VALUE obj2) if (obj1 == obj2) return TRUE; result = rb_eql_opt(obj1, obj2); - if (result == Qundef) { + if (UNDEF_P(result)) { result = rb_funcall(obj1, id_eql, 1, obj2); } return RTEST(result); @@ -297,7 +297,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) rb_shape_t * initial_shape = rb_shape_get_shape(dest); if (initial_shape->size_pool_index != src_shape->size_pool_index) { - RUBY_ASSERT(initial_shape->parent_id == ROOT_SHAPE_ID || initial_shape->type == SHAPE_ROOT); + RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT); shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape); } @@ -310,7 +310,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) rb_shape_t * initial_shape = rb_shape_get_shape(dest); if (initial_shape->size_pool_index != src_shape->size_pool_index) { - RUBY_ASSERT(initial_shape->parent_id == ROOT_SHAPE_ID || initial_shape->type == SHAPE_ROOT); + RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT); shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape); } @@ -421,7 +421,7 @@ rb_get_freeze_opt(int argc, VALUE *argv) rb_scan_args(argc, argv, "0:", &opt); if (!NIL_P(opt)) { rb_get_kwargs(opt, keyword_ids, 0, 1, &kwfreeze); - if (kwfreeze != Qundef) + if (!UNDEF_P(kwfreeze)) kwfreeze = obj_freeze_opt(kwfreeze); } return kwfreeze; @@ -2555,7 +2555,7 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod) #if 0 mod = rb_const_search(mod, id, beglen > 0 || !RTEST(recur), RTEST(recur), FALSE); - if (mod == Qundef) return Qfalse; + if (UNDEF_P(mod)) return Qfalse; #else if (!RTEST(recur)) { if (!rb_const_defined_at(mod, id)) @@ -2985,7 +2985,7 @@ static VALUE convert_type_with_id(VALUE val, const char *tname, ID method, int raise, int index) { VALUE r = rb_check_funcall(val, method, 0, 0); - if (r == Qundef) { + if (UNDEF_P(r)) { if (raise) { const char *msg = ((index < 0 ? conv_method_index(rb_id2name(method)) : index) diff --git a/parse.y b/parse.y index 526a25ac3b861d..846136bd0363de 100644 --- a/parse.y +++ b/parse.y @@ -5322,6 +5322,9 @@ args_tail : f_kwarg ',' f_kwrest opt_f_block_arg { add_forwarding_args(p); $$ = new_args_tail(p, Qnone, $1, ID2VAL(idFWD_BLOCK), &@1); + /*%%%*/ + ($$->nd_ainfo)->forwarding = 1; + /*% %*/ } ; @@ -5612,7 +5615,7 @@ f_kwrest : kwrest_mark tIDENTIFIER { arg_var(p, ANON_KEYWORD_REST_ID); /*%%%*/ - $$ = internal_id(p); + $$ = ANON_KEYWORD_REST_ID; /*% %*/ /*% ripper: kwrest_param!(Qnil) %*/ } @@ -5688,7 +5691,7 @@ f_rest_arg : restarg_mark tIDENTIFIER { arg_var(p, ANON_REST_ID); /*%%%*/ - $$ = internal_id(p); + $$ = ANON_REST_ID; /*% %*/ /*% ripper: rest_param!(Qnil) %*/ } @@ -5710,7 +5713,7 @@ f_block_arg : blkarg_mark tIDENTIFIER { arg_var(p, ANON_BLOCK_ID); /*%%%*/ - $$ = internal_id(p); + $$ = ANON_BLOCK_ID; /*% %*/ /*% ripper: blockarg!(Qnil) %*/ } @@ -10848,7 +10851,7 @@ check_literal_when(struct parser_params *p, NODE *arg, const YYLTYPE *loc) if (!arg || !p->case_labels) return; lit = rb_node_case_when_optimizable_literal(arg); - if (lit == Qundef) return; + if (UNDEF_P(lit)) return; if (nd_type_p(arg, NODE_STR)) { RB_OBJ_WRITTEN(p->ast, Qnil, arg->nd_lit = lit); } @@ -11501,7 +11504,7 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable, } if (RTEST(lit)) { VALUE e = shareable_literal_value(elt); - if (e != Qundef) { + if (!UNDEF_P(e)) { rb_ary_push(lit, e); } else { @@ -11541,7 +11544,7 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable, if (RTEST(lit)) { VALUE k = shareable_literal_value(key); VALUE v = shareable_literal_value(val); - if (k != Qundef && v != Qundef) { + if (!UNDEF_P(k) && !UNDEF_P(v)) { rb_hash_aset(lit, k, v); } else { @@ -12204,7 +12207,7 @@ new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, N int saved_line = p->ruby_sourceline; struct rb_args_info *args = tail->nd_ainfo; - if (args->block_arg == idFWD_BLOCK) { + if (args->forwarding) { if (rest_arg) { yyerror1(&tail->nd_loc, "... after rest argument"); return tail; @@ -12223,7 +12226,7 @@ new_args(struct parser_params *p, NODE *pre_args, NODE *opt_args, ID rest_arg, N args->opt_args = opt_args; - args->ruby2_keywords = rest_arg == idFWD_REST; + args->ruby2_keywords = args->forwarding; p->ruby_sourceline = saved_line; nd_set_loc(tail, loc); @@ -13787,7 +13790,7 @@ ripper_validate_object(VALUE self, VALUE x) if (x == Qfalse) return x; if (x == Qtrue) return x; if (NIL_P(x)) return x; - if (x == Qundef) + if (UNDEF_P(x)) rb_raise(rb_eArgError, "Qundef given"); if (FIXNUM_P(x)) return x; if (SYMBOL_P(x)) return x; @@ -13898,7 +13901,7 @@ static VALUE ripper_get_value(VALUE v) { NODE *nd; - if (v == Qundef) return Qnil; + if (UNDEF_P(v)) return Qnil; if (!RB_TYPE_P(v, T_NODE)) return v; nd = (NODE *)v; if (!nd_type_p(nd, NODE_RIPPER)) return Qnil; @@ -14159,7 +14162,7 @@ static VALUE ripper_assert_Qundef(VALUE self, VALUE obj, VALUE msg) { StringValue(msg); - if (obj == Qundef) { + if (UNDEF_P(obj)) { rb_raise(rb_eArgError, "%"PRIsVALUE, msg); } return Qnil; diff --git a/proc.c b/proc.c index a25786c0157251..71b6f1ef803606 100644 --- a/proc.c +++ b/proc.c @@ -89,7 +89,7 @@ block_mark(const struct rb_block *block) const struct rb_captured_block *captured = &block->as.captured; RUBY_MARK_MOVABLE_UNLESS_NULL(captured->self); RUBY_MARK_MOVABLE_UNLESS_NULL((VALUE)captured->code.val); - if (captured->ep && captured->ep[VM_ENV_DATA_INDEX_ENV] != Qundef /* cfunc_proc_t */) { + if (captured->ep && !UNDEF_P(captured->ep[VM_ENV_DATA_INDEX_ENV]) /* cfunc_proc_t */) { rb_gc_mark(VM_ENV_ENVVAL(captured->ep)); } } @@ -1645,7 +1645,7 @@ respond_to_missing_p(VALUE klass, VALUE obj, VALUE sym, int scope) /* TODO: merge with obj_respond_to() */ ID rmiss = idRespond_to_missing; - if (obj == Qundef) return 0; + if (UNDEF_P(obj)) return 0; if (rb_method_basic_definition_p(klass, rmiss)) return 0; return RTEST(rb_funcall(obj, rmiss, 2, sym, RBOOL(!scope))); } @@ -1746,7 +1746,7 @@ mnew_callable(VALUE klass, VALUE obj, ID id, VALUE mclass, int scope) const rb_method_entry_t *me; VALUE iclass = Qnil; - ASSUME(obj != Qundef); + ASSUME(!UNDEF_P(obj)); me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(klass, id, &iclass); return mnew_from_me(me, klass, iclass, obj, id, mclass, scope); } @@ -1988,7 +1988,7 @@ rb_method_name_error(VALUE klass, VALUE str) else if (RB_TYPE_P(c, T_MODULE)) { s = MSG(" module"); } - if (s == Qundef) { + if (UNDEF_P(s)) { s = MSG(" class"); } rb_name_err_raise_str(s, c, str); @@ -2493,7 +2493,7 @@ rb_method_call_with_block_kw(int argc, const VALUE *argv, VALUE method, VALUE pa rb_execution_context_t *ec = GET_EC(); TypedData_Get_Struct(method, struct METHOD, &method_data_type, data); - if (data->recv == Qundef) { + if (UNDEF_P(data->recv)) { rb_raise(rb_eTypeError, "can't call unbound method; bind first"); } return call_method_data(ec, data, argc, argv, passed_procval, kw_splat); @@ -3142,7 +3142,7 @@ method_inspect(VALUE method) if (FL_TEST(mklass, FL_SINGLETON)) { VALUE v = rb_ivar_get(mklass, attached); - if (data->recv == Qundef) { + if (UNDEF_P(data->recv)) { rb_str_buf_append(str, rb_inspect(mklass)); } else if (data->recv == v) { diff --git a/process.c b/process.c index d95b717960d097..dbe2be5b56fbe3 100644 --- a/process.c +++ b/process.c @@ -1332,7 +1332,7 @@ rb_process_status_wait(rb_pid_t pid, int flags) if (!(flags & WNOHANG)) { VALUE scheduler = rb_fiber_scheduler_current(); VALUE result = rb_fiber_scheduler_process_wait(scheduler, pid, flags); - if (result != Qundef) return result; + if (!UNDEF_P(result)) return result; } struct waitpid_state waitpid_state; @@ -3144,9 +3144,10 @@ NORETURN(static VALUE f_exec(int c, const VALUE *a, VALUE _)); * If the string from the first form (exec("command")) follows * these simple rules: * - * * no meta characters - * * not starting with shell reserved word or special built-in - * * Ruby invokes the command directly without shell + * * no meta characters, + * * not starting with shell reserved word or special built-in, + * + * Ruby invokes the command directly without shell. * * You can force shell invocation by adding ";" to the string (because ";" is * a meta character). diff --git a/ractor.c b/ractor.c index 3569202e58ee27..3605da74e6ffda 100644 --- a/ractor.c +++ b/ractor.c @@ -728,7 +728,7 @@ ractor_receive(rb_execution_context_t *ec, rb_ractor_t *cr) VM_ASSERT(cr == rb_ec_ractor_ptr(ec)); VALUE v; - while ((v = ractor_try_receive(ec, cr)) == Qundef) { + while (UNDEF_P(v = ractor_try_receive(ec, cr))) { ractor_receive_wait(ec, cr); } @@ -875,7 +875,7 @@ ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b) } RACTOR_UNLOCK_SELF(cr); - if (v != Qundef) { + if (!UNDEF_P(v)) { struct receive_block_data data = { .cr = cr, .rq = rq, @@ -887,7 +887,7 @@ ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b) VALUE result = rb_ensure(receive_if_body, (VALUE)&data, receive_if_ensure, (VALUE)&data); - if (result != Qundef) return result; + if (!UNDEF_P(result)) return result; index++; } @@ -1095,7 +1095,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL int i; bool interrupted = false; enum rb_ractor_wait_status wait_status = 0; - bool yield_p = (yielded_value != Qundef) ? true : false; + bool yield_p = !UNDEF_P(yielded_value) ? true : false; const int alen = rs_len + (yield_p ? 1 : 0); struct ractor_select_action { @@ -1152,7 +1152,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL case ractor_select_action_take: rv = actions[i].v; v = ractor_try_take(ec, RACTOR_PTR(rv)); - if (v != Qundef) { + if (!UNDEF_P(v)) { *ret_r = rv; ret = v; goto cleanup; @@ -1160,7 +1160,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL break; case ractor_select_action_receive: v = ractor_try_receive(ec, cr); - if (v != Qundef) { + if (!UNDEF_P(v)) { *ret_r = ID2SYM(rb_intern("receive")); ret = v; goto cleanup; @@ -1324,7 +1324,7 @@ ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VAL goto restart; } - VM_ASSERT(ret != Qundef); + VM_ASSERT(!UNDEF_P(ret)); return ret; } @@ -2289,7 +2289,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data) rb_ivar_generic_ivtbl_lookup(obj, &ivtbl); for (uint32_t i = 0; i < ivtbl->numiv; i++) { VALUE val = ivtbl->ivptr[i]; - if (val != Qundef && obj_traverse_i(val, data)) return 1; + if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1; } } @@ -2311,7 +2311,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data) for (uint32_t i=0; inumiv; i++) { - if (ivtbl->ivptr[i] != Qundef) { + if (!UNDEF_P(ivtbl->ivptr[i])) { CHECK_AND_REPLACE(ivtbl->ivptr[i]); } } @@ -2764,7 +2764,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data) VALUE *ptr = ROBJECT_IVPTR(obj); for (uint32_t i=0; imain_cache != Qundef) { + if (!UNDEF_P((VALUE)key->main_cache)) { *pret = key->main_cache; return true; } diff --git a/ractor_core.h b/ractor_core.h index 9d4b8387c734b0..294708fcb8ad1e 100644 --- a/ractor_core.h +++ b/ractor_core.h @@ -290,7 +290,7 @@ rb_ractor_id(const rb_ractor_t *r) #if RACTOR_CHECK_MODE > 0 uint32_t rb_ractor_current_id(void); // If ractor check mode is enabled, shape bits needs to be smaller -STATIC_ASSERT(shape_bits, SHAPE_BITS == 16); +STATIC_ASSERT(shape_bits, SHAPE_ID_NUM_BITS == 16); static inline void rb_ractor_setup_belonging_to(VALUE obj, uint32_t rid) diff --git a/range.c b/range.c index a2c0e218600607..0c3dc51c73b520 100644 --- a/range.c +++ b/range.c @@ -607,7 +607,7 @@ is_integer_p(VALUE v) VALUE is_int; CONST_ID(id_integer_p, "integer?"); is_int = rb_check_funcall(v, id_integer_p, 0, 0); - return RTEST(is_int) && is_int != Qundef; + return RTEST(is_int) && !UNDEF_P(is_int); } static VALUE @@ -1505,11 +1505,11 @@ rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp) else { VALUE x; b = rb_check_funcall(range, id_beg, 0, 0); - if (b == Qundef) return (int)Qfalse; + if (UNDEF_P(b)) return (int)Qfalse; e = rb_check_funcall(range, id_end, 0, 0); - if (e == Qundef) return (int)Qfalse; + if (UNDEF_P(e)) return (int)Qfalse; x = rb_check_funcall(range, rb_intern("exclude_end?"), 0, 0); - if (x == Qundef) return (int)Qfalse; + if (UNDEF_P(x)) return (int)Qfalse; excl = RTEST(x); } *begp = b; @@ -1646,7 +1646,7 @@ inspect_range(VALUE range, VALUE dummy, int recur) if (NIL_P(RANGE_BEG(range)) || !NIL_P(RANGE_END(range))) { str2 = rb_inspect(RANGE_END(range)); } - if (str2 != Qundef) rb_str_append(str, str2); + if (!UNDEF_P(str2)) rb_str_append(str, str2); return str; } @@ -1724,7 +1724,7 @@ static VALUE range_eqq(VALUE range, VALUE val) { VALUE ret = range_include_internal(range, val, 1); - if (ret != Qundef) return ret; + if (!UNDEF_P(ret)) return ret; return r_cover_p(range, RANGE_BEG(range), RANGE_END(range), val); } @@ -1764,7 +1764,7 @@ static VALUE range_include(VALUE range, VALUE val) { VALUE ret = range_include_internal(range, val, 0); - if (ret != Qundef) return ret; + if (!UNDEF_P(ret)) return ret; return rb_call_super(1, &val); } diff --git a/rational.c b/rational.c index 48a9ab2ed28932..dfe2ad74eb7b9b 100644 --- a/rational.c +++ b/rational.c @@ -2552,7 +2552,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) VALUE a1 = numv, a2 = denv; int state; - assert(a1 != Qundef); + assert(!UNDEF_P(a1)); if (NIL_P(a1) || NIL_P(a2)) { if (!raise) return Qnil; @@ -2603,7 +2603,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) a2 = string_to_r_strict(a2, raise); if (!raise && NIL_P(a2)) return Qnil; } - else if (a2 != Qundef && !rb_respond_to(a2, idTo_r)) { + else if (!UNDEF_P(a2) && !rb_respond_to(a2, idTo_r)) { VALUE tmp = rb_protect(rb_check_to_int, a2, NULL); rb_set_errinfo(Qnil); if (!NIL_P(tmp)) { @@ -2612,11 +2612,11 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) } if (RB_TYPE_P(a1, T_RATIONAL)) { - if (a2 == Qundef || (k_exact_one_p(a2))) + if (UNDEF_P(a2) || (k_exact_one_p(a2))) return a1; } - if (a2 == Qundef) { + if (UNDEF_P(a2)) { if (!RB_INTEGER_TYPE_P(a1)) { if (!raise) { VALUE result = rb_protect(to_rational, a1, NULL); @@ -2666,7 +2666,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) a1 = nurat_int_value(a1); - if (a2 == Qundef) { + if (UNDEF_P(a2)) { a2 = ONE; } else if (!k_integer_p(a2) && !raise) { diff --git a/re.c b/re.c index 1b8314832156a5..15fe10b6af725c 100644 --- a/re.c +++ b/re.c @@ -3832,14 +3832,14 @@ rb_reg_initialize_m(int argc, VALUE *argv, VALUE self) str = RREGEXP_SRC(re); } else { - if (opts != Qundef) { + if (!UNDEF_P(opts)) { int f; if (FIXNUM_P(opts)) flags = FIX2INT(opts); else if ((f = str_to_option(opts)) >= 0) flags = f; else if (!NIL_P(opts) && rb_bool_expected(opts, "ignorecase", FALSE)) flags = ONIG_OPTION_IGNORECASE; } - if (n_flag != Qundef && !NIL_P(n_flag)) { + if (!UNDEF_P(n_flag) && !NIL_P(n_flag)) { char *kcode = StringValuePtr(n_flag); if (kcode[0] == 'n' || kcode[0] == 'N') { enc = rb_ascii8bit_encoding(); diff --git a/regexec.c b/regexec.c index 75ffb86c4510dc..f7c5d12133ff5b 100644 --- a/regexec.c +++ b/regexec.c @@ -234,10 +234,8 @@ onig_get_capture_tree(OnigRegion* region) #ifdef USE_CACHE_MATCH_OPT /* count number of jump-like opcodes for allocation of cache memory. */ -/* return -1 if we cannot optimize the regex matching by using cache. */ -static long count_num_cache_opcode(regex_t* reg, long* table_size) +static OnigPosition count_num_cache_opcode(regex_t* reg, long* num, long* table_size) { - long num = 0; UChar* p = reg->p; UChar* pend = p + reg->used; LengthType len; @@ -246,6 +244,9 @@ static long count_num_cache_opcode(regex_t* reg, long* table_size) long current_mem_num = 0; OnigEncoding enc = reg->enc; + *num = 0; + *table_size = 0; + while (p < pend) { switch (*p++) { case OP_FINISH: @@ -298,10 +299,10 @@ static long count_num_cache_opcode(regex_t* reg, long* table_size) break; case OP_ANYCHAR_STAR: case OP_ANYCHAR_ML_STAR: - num++; *table_size += 1; break; + *num += 1; *table_size += 1; break; case OP_ANYCHAR_STAR_PEEK_NEXT: case OP_ANYCHAR_ML_STAR_PEEK_NEXT: - p++; num++; *table_size += 1; break; + p++; *num += 1; *table_size += 1; break; case OP_WORD: case OP_NOT_WORD: @@ -334,7 +335,7 @@ static long count_num_cache_opcode(regex_t* reg, long* table_size) case OP_BACKREF_MULTI: case OP_BACKREF_MULTI_IC: case OP_BACKREF_WITH_LEVEL: - return NUM_CACHE_OPCODE_FAIL; + goto fail; case OP_MEMORY_START: case OP_MEMORY_START_PUSH: @@ -354,43 +355,43 @@ static long count_num_cache_opcode(regex_t* reg, long* table_size) break; case OP_PUSH: p += SIZE_RELADDR; - num++; + *num += 1; *table_size += 1; break; case OP_POP: break; case OP_PUSH_OR_JUMP_EXACT1: case OP_PUSH_IF_PEEK_NEXT: - p += SIZE_RELADDR + 1; num++; *table_size += 1; break; + p += SIZE_RELADDR + 1; *num += 1; *table_size += 1; break; case OP_REPEAT: case OP_REPEAT_NG: if (current_mem != -1) { // A nested OP_REPEAT is not yet supported. - return NUM_CACHE_OPCODE_FAIL; + goto fail; } GET_MEMNUM_INC(mem, p); p += SIZE_RELADDR; if (reg->repeat_range[mem].lower == 0) { - num++; + *num += 1; *table_size += 1; } - reg->repeat_range[mem].base_num = num; + reg->repeat_range[mem].base_num = *num; current_mem = mem; - current_mem_num = num; + current_mem_num = *num; break; case OP_REPEAT_INC: case OP_REPEAT_INC_NG: GET_MEMNUM_INC(mem, p); if (mem != current_mem) { // A lone or invalid OP_REPEAT_INC is found. - return NUM_CACHE_OPCODE_FAIL; + goto fail; } { - long inner_num = num - current_mem_num; + long inner_num = *num - current_mem_num; OnigRepeatRange *repeat_range = ®->repeat_range[mem]; repeat_range->inner_num = inner_num; - num -= inner_num; - num += inner_num * repeat_range->lower + (inner_num + 1) * (repeat_range->upper == 0x7fffffff ? 1 : repeat_range->upper - repeat_range->lower); + *num -= inner_num; + *num += inner_num * repeat_range->lower + (inner_num + 1) * (repeat_range->upper == 0x7fffffff ? 1 : repeat_range->upper - repeat_range->lower); if (repeat_range->lower < repeat_range->upper) { *table_size += 1; } @@ -401,7 +402,7 @@ static long count_num_cache_opcode(regex_t* reg, long* table_size) case OP_REPEAT_INC_SG: case OP_REPEAT_INC_NG_SG: // TODO: Support nested OP_REPEAT. - return NUM_CACHE_OPCODE_FAIL; + goto fail; case OP_NULL_CHECK_START: case OP_NULL_CHECK_END: case OP_NULL_CHECK_END_MEMST: @@ -420,33 +421,43 @@ static long count_num_cache_opcode(regex_t* reg, long* table_size) case OP_PUSH_ABSENT_POS: case OP_ABSENT_END: case OP_ABSENT: - return NUM_CACHE_OPCODE_FAIL; + goto fail; case OP_CALL: case OP_RETURN: - return NUM_CACHE_OPCODE_FAIL; + goto fail; case OP_CONDITION: - return NUM_CACHE_OPCODE_FAIL; + goto fail; case OP_STATE_CHECK_PUSH: case OP_STATE_CHECK_PUSH_OR_JUMP: case OP_STATE_CHECK: case OP_STATE_CHECK_ANYCHAR_STAR: case OP_STATE_CHECK_ANYCHAR_ML_STAR: - return NUM_CACHE_OPCODE_FAIL; + goto fail; case OP_SET_OPTION_PUSH: case OP_SET_OPTION: p += SIZE_OPTION; break; + + default: + goto bytecode_error; } } - return num; + return 0; + +fail: + *num = NUM_CACHE_OPCODE_FAIL; + return 0; + +bytecode_error: + return ONIGERR_UNDEFINED_BYTECODE; } -static void init_cache_index_table(regex_t* reg, OnigCacheIndex *table) +static OnigPosition init_cache_index_table(regex_t* reg, OnigCacheIndex *table) { UChar* pbegin; UChar* p = reg->p; @@ -496,6 +507,7 @@ static void init_cache_index_table(regex_t* reg, OnigCacheIndex *table) case OP_CCLASS: case OP_CCLASS_NOT: p += SIZE_BITSET; break; + case OP_CCLASS_MB: case OP_CCLASS_MB_NOT: GET_LENGTH_INC(len, p); p += len; break; case OP_CCLASS_MIX: @@ -557,7 +569,7 @@ static void init_cache_index_table(regex_t* reg, OnigCacheIndex *table) case OP_BACKREF_MULTI: case OP_BACKREF_MULTI_IC: case OP_BACKREF_WITH_LEVEL: - return; + goto unexpected_bytecode_error; case OP_MEMORY_START: case OP_MEMORY_START_PUSH: @@ -629,7 +641,7 @@ static void init_cache_index_table(regex_t* reg, OnigCacheIndex *table) case OP_REPEAT_INC_SG: case OP_REPEAT_INC_NG_SG: // TODO: support OP_REPEAT opcodes. - return; + goto unexpected_bytecode_error; case OP_NULL_CHECK_START: case OP_NULL_CHECK_END: case OP_NULL_CHECK_END_MEMST: @@ -648,28 +660,39 @@ static void init_cache_index_table(regex_t* reg, OnigCacheIndex *table) case OP_PUSH_ABSENT_POS: case OP_ABSENT_END: case OP_ABSENT: - return; + goto unexpected_bytecode_error; case OP_CALL: case OP_RETURN: - return; + goto unexpected_bytecode_error; case OP_CONDITION: - return; + goto unexpected_bytecode_error; case OP_STATE_CHECK_PUSH: case OP_STATE_CHECK_PUSH_OR_JUMP: case OP_STATE_CHECK: case OP_STATE_CHECK_ANYCHAR_STAR: case OP_STATE_CHECK_ANYCHAR_ML_STAR: - return; + goto unexpected_bytecode_error; case OP_SET_OPTION_PUSH: case OP_SET_OPTION: p += SIZE_OPTION; break; + + default: + goto bytecode_error; } } + + return 0; + +unexpected_bytecode_error: + return ONIGERR_UNEXPECTED_BYTECODE; + +bytecode_error: + return ONIGERR_UNDEFINED_BYTECODE; } #endif /* USE_MATCH_CACHE */ @@ -860,6 +883,7 @@ onig_region_copy(OnigRegion* to, const OnigRegion* from) (msa).enable_cache_match_opt = 0;\ (msa).num_fail = 0;\ (msa).num_cache_opcode = NUM_CACHE_OPCODE_UNINIT;\ + (msa).num_cache_table = 0;\ (msa).cache_index_table = (OnigCacheIndex *)0;\ (msa).match_cache = (uint8_t *)0;\ } while(0) @@ -3819,23 +3843,26 @@ match_at(regex_t* reg, const UChar* str, const UChar* end, #ifdef USE_CACHE_MATCH_OPT if (++msa->num_fail >= (long)(end - str) + 1 && msa->num_cache_opcode == NUM_CACHE_OPCODE_UNINIT) { - long table_size = 0; msa->enable_cache_match_opt = 1; if (msa->num_cache_opcode == NUM_CACHE_OPCODE_UNINIT) { - msa->num_cache_opcode = count_num_cache_opcode(reg, &table_size); + OnigPosition r = count_num_cache_opcode(reg, &msa->num_cache_opcode, &msa->num_cache_table); + if (r < 0) goto bytecode_error; } if (msa->num_cache_opcode == NUM_CACHE_OPCODE_FAIL || msa->num_cache_opcode == 0) { msa->enable_cache_match_opt = 0; goto fail_match_cache_opt; } if (msa->cache_index_table == NULL) { - OnigCacheIndex *table = (OnigCacheIndex *)xmalloc(table_size * sizeof(OnigCacheIndex)); + OnigCacheIndex *table = (OnigCacheIndex *)xmalloc(msa->num_cache_table * sizeof(OnigCacheIndex)); if (table == NULL) { return ONIGERR_MEMORY; } - init_cache_index_table(reg, table); + OnigPosition r = init_cache_index_table(reg, table); + if (r < 0) { + if (r == ONIGERR_UNEXPECTED_BYTECODE) goto unexpected_bytecode_error; + else goto bytecode_error; + } msa->cache_index_table = table; - msa->num_cache_table = table_size; } size_t len = (end - str) + 1; size_t match_cache_size8 = (size_t)msa->num_cache_opcode * len; diff --git a/scheduler.c b/scheduler.c index 611182741c664b..9f28ae0eb6dffb 100644 --- a/scheduler.c +++ b/scheduler.c @@ -231,10 +231,10 @@ rb_fiber_scheduler_close(VALUE scheduler) // would create an infinite loop. result = rb_check_funcall(scheduler, id_scheduler_close, 0, NULL); - if (result != Qundef) return result; + if (!UNDEF_P(result)) return result; result = rb_check_funcall(scheduler, id_close, 0, NULL); - if (result != Qundef) return result; + if (!UNDEF_P(result)) return result; return Qnil; } diff --git a/shape.c b/shape.c index 66f5f69a0c6de3..430bfeb90a4f49 100644 --- a/shape.c +++ b/shape.c @@ -8,6 +8,7 @@ #include static ID id_frozen; +static ID id_t_object; static ID size_pool_edge_names[SIZE_POOL_COUNT]; /* @@ -152,6 +153,7 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type) case SHAPE_CAPACITY_CHANGE: case SHAPE_IVAR_UNDEF: case SHAPE_FROZEN: + case SHAPE_T_OBJECT: new_shape->next_iv_index = shape->next_iv_index; break; case SHAPE_INITIAL_CAPACITY: @@ -264,6 +266,7 @@ rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) case SHAPE_IVAR_UNDEF: case SHAPE_ROOT: case SHAPE_INITIAL_CAPACITY: + case SHAPE_T_OBJECT: return false; case SHAPE_FROZEN: rb_bug("Ivar should not exist on transition\n"); @@ -322,10 +325,10 @@ rb_shape_set_shape(VALUE obj, rb_shape_t* shape) rb_shape_set_shape_id(obj, rb_shape_id(shape)); } -VALUE -rb_shape_flags_mask(void) +uint8_t +rb_shape_id_num_bits(void) { - return SHAPE_FLAG_MASK; + return SHAPE_ID_NUM_BITS; } rb_shape_t * @@ -333,29 +336,33 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape) { rb_shape_t * midway_shape; - if (dest_shape->type != SHAPE_ROOT) { + RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT); + + if (dest_shape->type != initial_shape->type) { midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_parent(dest_shape)); } else { midway_shape = initial_shape; } - switch (dest_shape->type) { - case SHAPE_IVAR: - if (midway_shape->capacity < midway_shape->next_iv_index) { - // There isn't enough room to write this IV, so we need to increase the capacity - midway_shape = rb_shape_transition_shape_capa(midway_shape, midway_shape->capacity * 2); - } + switch ((enum shape_type)dest_shape->type) { + case SHAPE_IVAR: + if (midway_shape->capacity <= midway_shape->next_iv_index) { + // There isn't enough room to write this IV, so we need to increase the capacity + midway_shape = rb_shape_transition_shape_capa(midway_shape, midway_shape->capacity * 2); + } - midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name); - break; - case SHAPE_IVAR_UNDEF: - midway_shape = get_next_shape_internal(midway_shape, dest_shape->edge_name, SHAPE_IVAR_UNDEF); - break; - case SHAPE_ROOT: - case SHAPE_FROZEN: - case SHAPE_CAPACITY_CHANGE: - break; + midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name); + break; + case SHAPE_IVAR_UNDEF: + midway_shape = get_next_shape_internal(midway_shape, dest_shape->edge_name, SHAPE_IVAR_UNDEF); + break; + case SHAPE_ROOT: + case SHAPE_FROZEN: + case SHAPE_CAPACITY_CHANGE: + case SHAPE_INITIAL_CAPACITY: + case SHAPE_T_OBJECT: + break; } return midway_shape; @@ -592,6 +599,7 @@ void Init_default_shapes(void) { id_frozen = rb_make_internal_id(); + id_t_object = rb_make_internal_id(); // Shapes by size pool for (int i = 0; i < SIZE_POOL_COUNT; i++) { @@ -615,6 +623,16 @@ Init_default_shapes(void) RUBY_ASSERT(rb_shape_id(new_shape) == (shape_id_t)i); } + // Make shapes for T_OBJECT + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_shape_t * shape = rb_shape_get_shape_by_id(i); +#if RUBY_DEBUG + rb_shape_t * t_object_shape = +#endif + get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT); + RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT)); + } + // Special const shape #if RUBY_DEBUG rb_shape_t * special_const_shape = @@ -644,9 +662,10 @@ Init_shape(void) rb_define_method(rb_cShape, "capacity", rb_shape_capacity, 0); rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT)); rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR)); + rb_define_const(rb_cShape, "SHAPE_T_OBJECT", INT2NUM(SHAPE_T_OBJECT)); rb_define_const(rb_cShape, "SHAPE_IVAR_UNDEF", INT2NUM(SHAPE_IVAR_UNDEF)); rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN)); - rb_define_const(rb_cShape, "SHAPE_BITS", INT2NUM(SHAPE_BITS)); + rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS)); rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT)); rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID)); diff --git a/shape.h b/shape.h index a2cbd7c482ee86..43308b833baf20 100644 --- a/shape.h +++ b/shape.h @@ -15,25 +15,25 @@ typedef uint16_t attr_index_t; #if RUBY_DEBUG || (defined(VM_CHECK_MODE) && VM_CHECK_MODE > 0) # if SIZEOF_SHAPE_T == 4 typedef uint32_t shape_id_t; -# define SHAPE_BITS 16 +# define SHAPE_ID_NUM_BITS 16 # else typedef uint16_t shape_id_t; -# define SHAPE_BITS 16 +# define SHAPE_ID_NUM_BITS 16 # endif #else # if SIZEOF_SHAPE_T == 4 typedef uint32_t shape_id_t; -# define SHAPE_BITS 32 +# define SHAPE_ID_NUM_BITS 32 # else typedef uint16_t shape_id_t; -# define SHAPE_BITS 16 +# define SHAPE_ID_NUM_BITS 16 # endif #endif -# define SHAPE_MASK (((uintptr_t)1 << SHAPE_BITS) - 1) -# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_BITS) +# define SHAPE_MASK (((uintptr_t)1 << SHAPE_ID_NUM_BITS) - 1) +# define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS) -# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_BITS) +# define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * 8) - SHAPE_ID_NUM_BITS) # define SHAPE_BITMAP_SIZE 16384 @@ -42,7 +42,7 @@ typedef uint16_t shape_id_t; # define ROOT_SHAPE_ID 0x0 // We use SIZE_POOL_COUNT number of shape IDs for transitions out of different size pools // The next available shapd ID will be the SPECIAL_CONST_SHAPE_ID -# define SPECIAL_CONST_SHAPE_ID SIZE_POOL_COUNT +# define SPECIAL_CONST_SHAPE_ID (SIZE_POOL_COUNT * 2) struct rb_shape { struct rb_id_table * edges; // id_table from ID (ivar) to next shape @@ -63,6 +63,7 @@ enum shape_type { SHAPE_CAPACITY_CHANGE, SHAPE_IVAR_UNDEF, SHAPE_INITIAL_CAPACITY, + SHAPE_T_OBJECT, }; #if SHAPE_IN_BASIC_FLAGS @@ -131,6 +132,7 @@ static inline shape_id_t RCLASS_SHAPE_ID(VALUE obj) { bool rb_shape_root_shape_p(rb_shape_t* shape); rb_shape_t * rb_shape_get_root_shape(void); +uint8_t rb_shape_id_num_bits(void); rb_shape_t* rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id); rb_shape_t * rb_shape_get_parent(rb_shape_t * shape); diff --git a/signal.c b/signal.c index 874afd28d5b3bc..04db34fc3dd6c3 100644 --- a/signal.c +++ b/signal.c @@ -1129,7 +1129,7 @@ rb_signal_exec(rb_thread_t *th, int sig) break; } } - else if (cmd == Qundef) { + else if (UNDEF_P(cmd)) { rb_threadptr_signal_exit(th); } else { diff --git a/sprintf.c b/sprintf.c index bfe25e1d3c8b05..b26dffec8570df 100644 --- a/sprintf.c +++ b/sprintf.c @@ -97,7 +97,7 @@ sign_bits(int base, const char *p) blen += (l);\ } while (0) -#define GETARG() (nextvalue != Qundef ? nextvalue : \ +#define GETARG() (!UNDEF_P(nextvalue) ? nextvalue : \ GETNEXTARG()) #define GETNEXTARG() ( \ @@ -193,7 +193,7 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv) { VALUE tmp; - if (*hash != Qundef) return *hash; + if (!UNDEF_P(*hash)) return *hash; if (argc != 2) { rb_raise(rb_eArgError, "one hash required"); } @@ -336,7 +336,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) n = 0; GETNUM(n, width); if (*p == '$') { - if (nextvalue != Qundef) { + if (!UNDEF_P(nextvalue)) { rb_raise(rb_eArgError, "value given twice - %d$", n); } nextvalue = GETPOSARG(n); @@ -381,7 +381,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) len - 2 /* without parenthesis */, enc); if (!NIL_P(sym)) nextvalue = rb_hash_lookup2(hash, sym, Qundef); - if (nextvalue == Qundef) { + if (UNDEF_P(nextvalue)) { if (NIL_P(sym)) { sym = rb_sym_intern(start + 1, len - 2 /* without parenthesis */, diff --git a/string.c b/string.c index 1ce4a07651dcaf..990fe8f8273751 100644 --- a/string.c +++ b/string.c @@ -499,7 +499,7 @@ register_fstring(VALUE str, bool copy) do { args.fstr = str; st_update(frozen_strings, (st_data_t)str, fstr_update_callback, (st_data_t)&args); - } while (args.fstr == Qundef); + } while (UNDEF_P(args.fstr)); } RB_VM_LOCK_LEAVE(); @@ -1877,10 +1877,10 @@ rb_str_init(int argc, VALUE *argv, VALUE str) rb_get_kwargs(opt, keyword_ids, 0, 2, kwargs); venc = kwargs[0]; vcapa = kwargs[1]; - if (venc != Qundef && !NIL_P(venc)) { + if (!UNDEF_P(venc) && !NIL_P(venc)) { enc = rb_to_encoding(venc); } - if (vcapa != Qundef && !NIL_P(vcapa)) { + if (!UNDEF_P(vcapa) && !NIL_P(vcapa)) { long capa = NUM2LONG(vcapa); long len = 0; int termlen = enc ? rb_enc_mbminlen(enc) : 1; @@ -8926,7 +8926,7 @@ rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, VALUE ary) keywords[0] = rb_intern_const("chomp"); } rb_get_kwargs(opts, keywords, 0, 1, &chomp); - chomp = (chomp != Qundef && RTEST(chomp)); + chomp = (!UNDEF_P(chomp) && RTEST(chomp)); } if (NIL_P(rs)) { @@ -11528,23 +11528,6 @@ rb_sym_to_s(VALUE sym) return str_new_shared(rb_cString, rb_sym2str(sym)); } -/* - * call-seq: - * to_sym -> self - * - * Returns +self+. - * - * Symbol#intern is an alias for Symbol#to_sym. - * - * Related: String#to_sym. - */ - -static VALUE -sym_to_sym(VALUE sym) -{ - return sym; -} - MJIT_FUNC_EXPORTED VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, int kw_splat, VALUE passed_proc) { @@ -12111,8 +12094,6 @@ Init_String(void) rb_define_method(rb_cSymbol, "to_s", rb_sym_to_s, 0); rb_define_method(rb_cSymbol, "id2name", rb_sym_to_s, 0); rb_define_method(rb_cSymbol, "name", rb_sym2str, 0); /* in symbol.c */ - rb_define_method(rb_cSymbol, "intern", sym_to_sym, 0); - rb_define_method(rb_cSymbol, "to_sym", sym_to_sym, 0); rb_define_method(rb_cSymbol, "to_proc", rb_sym_to_proc, 0); /* in proc.c */ rb_define_method(rb_cSymbol, "succ", sym_succ, 0); rb_define_method(rb_cSymbol, "next", sym_succ, 0); diff --git a/struct.c b/struct.c index da84fa6bcb7b76..10e5209fb989fd 100644 --- a/struct.c +++ b/struct.c @@ -650,7 +650,7 @@ rb_struct_s_def(int argc, VALUE *argv, VALUE klass) keyword_ids[0] = rb_intern("keyword_init"); } rb_get_kwargs(opt, keyword_ids, 0, 1, &keyword_init); - if (keyword_init == Qundef) { + if (UNDEF_P(keyword_init)) { keyword_init = Qnil; } else if (RTEST(keyword_init)) { diff --git a/symbol.c b/symbol.c index adcc1275e3e30c..cd6a3c3407609c 100644 --- a/symbol.c +++ b/symbol.c @@ -22,6 +22,7 @@ #include "ruby/st.h" #include "symbol.h" #include "vm_sync.h" +#include "builtin.h" #ifndef USE_SYMBOL_GC # define USE_SYMBOL_GC 1 @@ -1262,3 +1263,4 @@ rb_is_local_name(VALUE name) } #include "id_table.c" +#include "symbol.rbinc" diff --git a/symbol.rb b/symbol.rb new file mode 100644 index 00000000000000..0b23ebb267ea90 --- /dev/null +++ b/symbol.rb @@ -0,0 +1,15 @@ +class Symbol + # call-seq: + # to_sym -> self + # + # Returns +self+. + # + # Symbol#intern is an alias for Symbol#to_sym. + # + # Related: String#to_sym. + def to_sym + self + end + + alias intern to_sym +end diff --git a/test/fiber/test_enumerator.rb b/test/fiber/test_enumerator.rb index c635f474dbde49..40f7d0172531dc 100644 --- a/test/fiber/test_enumerator.rb +++ b/test/fiber/test_enumerator.rb @@ -10,12 +10,6 @@ def test_read_characters i, o = UNIXSocket.pair - unless i.nonblock? && o.nonblock? - i.close - o.close - omit "I/O is not non-blocking!" - end - message = String.new thread = Thread.new do diff --git a/test/fiber/test_io.rb b/test/fiber/test_io.rb index 821a169e44af72..de88745e57966c 100644 --- a/test/fiber/test_io.rb +++ b/test/fiber/test_io.rb @@ -6,14 +6,12 @@ class TestFiberIO < Test::Unit::TestCase MESSAGE = "Hello World" def test_read - omit "UNIXSocket is not defined!" unless defined?(UNIXSocket) + omit unless defined?(UNIXSocket) i, o = UNIXSocket.pair - - unless i.nonblock? && o.nonblock? - i.close - o.close - omit "I/O is not non-blocking!" + if RUBY_PLATFORM=~/mswin|mingw/ + i.nonblock = true + o.nonblock = true end message = nil @@ -46,6 +44,10 @@ def test_heavy_read 16.times.map do Thread.new do i, o = UNIXSocket.pair + if RUBY_PLATFORM=~/mswin|mingw/ + i.nonblock = true + o.nonblock = true + end scheduler = Scheduler.new Fiber.set_scheduler scheduler @@ -64,16 +66,11 @@ def test_heavy_read end def test_epipe_on_read - omit "UNIXSocket is not defined!" unless defined?(UNIXSocket) + omit unless defined?(UNIXSocket) + omit "nonblock=true isn't properly supported on Windows" if RUBY_PLATFORM=~/mswin|mingw/ i, o = UNIXSocket.pair - unless i.nonblock? && o.nonblock? - i.close - o.close - omit "I/O is not non-blocking!" - end - error = nil thread = Thread.new do diff --git a/test/fileutils/test_fileutils.rb b/test/fileutils/test_fileutils.rb index 05ba8d184ae307..2748bd247fe9b5 100644 --- a/test/fileutils/test_fileutils.rb +++ b/test/fileutils/test_fileutils.rb @@ -472,10 +472,14 @@ def test_cp_r_dev else def test_cp_r_socket pend "Skipping socket test on JRuby" if RUBY_ENGINE == 'jruby' + Dir.mkdir('tmp/cpr_src') UNIXServer.new('tmp/cpr_src/socket').close cp_r 'tmp/cpr_src', 'tmp/cpr_dest' assert_equal(true, File.socket?('tmp/cpr_dest/socket')) + rescue Errno::EINVAL => error + # On some platforms (windows) sockets cannot be copied by FileUtils. + omit error.message end if defined?(UNIXServer) end diff --git a/test/io/console/test_io_console.rb b/test/io/console/test_io_console.rb index 3c44181f2ac0b6..27d8e4a9888d49 100644 --- a/test/io/console/test_io_console.rb +++ b/test/io/console/test_io_console.rb @@ -440,7 +440,9 @@ def helper def run_pty(src, n = 1) pend("PTY.spawn cannot control terminal on JRuby") if RUBY_ENGINE == 'jruby' - r, w, pid = PTY.spawn(EnvUtil.rubybin, "-I#{TestIO_Console::PATHS.join(File::PATH_SEPARATOR)}", "-rio/console", "-e", src) + args = ["-I#{TestIO_Console::PATHS.join(File::PATH_SEPARATOR)}", "-rio/console", "-e", src] + args.shift if args.first == "-I" # statically linked + r, w, pid = PTY.spawn(EnvUtil.rubybin, *args) rescue RuntimeError omit $! else diff --git a/test/irb/helper.rb b/test/irb/helper.rb index 15d5dafc57ef9d..293315879a1e8c 100644 --- a/test/irb/helper.rb +++ b/test/irb/helper.rb @@ -1,7 +1,37 @@ require "test/unit" +module IRB + class InputMethod; end +end + module TestIRB class TestCase < Test::Unit::TestCase + class TestInputMethod < ::IRB::InputMethod + attr_reader :list, :line_no + + def initialize(list = []) + super("test") + @line_no = 0 + @list = list + end + + def gets + @list[@line_no]&.tap {@line_no += 1} + end + + def eof? + @line_no >= @list.size + end + + def encoding + Encoding.default_external + end + + def reset + @line_no = 0 + end + end + def save_encodings @default_encoding = [Encoding.default_external, Encoding.default_internal] @stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] } diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb index 3af4ccec98dc02..bcfb1d0b86511b 100644 --- a/test/irb/test_cmd.rb +++ b/test/irb/test_cmd.rb @@ -6,32 +6,6 @@ module TestIRB class ExtendCommandTest < TestCase - class TestInputMethod < ::IRB::InputMethod - attr_reader :list, :line_no - - def initialize(list = []) - super("test") - @line_no = 0 - @list = list - end - - def gets - @list[@line_no]&.tap {@line_no += 1} - end - - def eof? - @line_no >= @list.size - end - - def encoding - Encoding.default_external - end - - def reset - @line_no = 0 - end - end - def setup @pwd = Dir.pwd @tmpdir = File.join(Dir.tmpdir, "test_reline_config_#{$$}") @@ -549,7 +523,7 @@ def test_show_source_end_finder def show_source_test_method unless true end - end + end unless defined?(show_source_test_method) EOS out, err = execute_lines( diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index e7cc0bab4f4fd8..4d256a73a20e4c 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -7,32 +7,6 @@ module TestIRB class TestContext < TestCase - class TestInputMethod < ::IRB::InputMethod - attr_reader :list, :line_no - - def initialize(list = []) - super("test") - @line_no = 0 - @list = list - end - - def gets - @list[@line_no]&.tap {@line_no += 1} - end - - def eof? - @line_no >= @list.size - end - - def encoding - Encoding.default_external - end - - def reset - @line_no = 0 - end - end - def setup IRB.init_config(nil) IRB.conf[:USE_SINGLELINE] = false @@ -518,7 +492,7 @@ def test_eval_input_with_exception irb.eval_input end assert_empty err - if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' && STDOUT.tty? + if RUBY_VERSION < '3.0.0' && STDOUT.tty? expected = [ :*, /Traceback \(most recent call last\):\n/, :*, /\t 2: from \(irb\):1:in `
'\n/, @@ -549,7 +523,7 @@ def test_eval_input_with_invalid_byte_sequence_exception irb.eval_input end assert_empty err - if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' && STDOUT.tty? + if RUBY_VERSION < '3.0.0' && STDOUT.tty? expected = [ :*, /Traceback \(most recent call last\):\n/, :*, /\t 2: from \(irb\):1:in `
'\n/, @@ -586,7 +560,7 @@ def test_eval_input_with_long_exception irb.eval_input end assert_empty err - if '2.5.0' <= RUBY_VERSION && RUBY_VERSION < '3.0.0' && STDOUT.tty? + if RUBY_VERSION < '3.0.0' && STDOUT.tty? expected = [ :*, /Traceback \(most recent call last\):\n/, :*, /\t... \d+ levels...\n/, diff --git a/test/irb/test_history.rb b/test/irb/test_history.rb index b0a07ca29d3e8a..2ef4bb337351b6 100644 --- a/test/irb/test_history.rb +++ b/test/irb/test_history.rb @@ -15,38 +15,10 @@ def teardown IRB.conf[:RC_NAME_GENERATOR] = nil end - class TestInputMethod < ::IRB::InputMethod + class TestInputMethodWithHistory < TestInputMethod HISTORY = Array.new include IRB::HistorySavingAbility - - attr_reader :list, :line_no - - def initialize(list = []) - super("test") - @line_no = 0 - @list = list - end - - def gets - @list[@line_no]&.tap {@line_no += 1} - end - - def eof? - @line_no >= @list.size - end - - def encoding - Encoding.default_external - end - - def reset - @line_no = 0 - end - - def winsize - [10, 20] - end end def test_history_save_1 @@ -167,7 +139,7 @@ def test_history_concurrent_use_not_present IRB.conf[:SAVE_HISTORY] = 1 Dir.mktmpdir("test_irb_history_") do |tmpdir| ENV["HOME"] = tmpdir - io = TestInputMethod.new + io = TestInputMethodWithHistory.new io.class::HISTORY.clear io.load_history io.class::HISTORY.concat(%w"line1 line2") @@ -198,7 +170,7 @@ def assert_history(expected_history, initial_irb_history, input) f.write(initial_irb_history) end - io = TestInputMethod.new + io = TestInputMethodWithHistory.new io.class::HISTORY.clear io.load_history if block_given? diff --git a/test/irb/test_raise_no_backtrace_exception.rb b/test/irb/test_raise_no_backtrace_exception.rb index ba8e89c59fb63b..9565419cdd0d64 100644 --- a/test/irb/test_raise_no_backtrace_exception.rb +++ b/test/irb/test_raise_no_backtrace_exception.rb @@ -1,4 +1,5 @@ # frozen_string_literal: false +require "tmpdir" require_relative "helper" diff --git a/test/irb/test_ruby_lex.rb b/test/irb/test_ruby_lex.rb index 2b16e2e9a10b6c..fa68a4632cb968 100644 --- a/test/irb/test_ruby_lex.rb +++ b/test/irb/test_ruby_lex.rb @@ -181,9 +181,6 @@ def test_symbols end def test_endless_range_at_end_of_line - if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.6.0') - pend 'Endless range is available in 2.6.0 or later' - end input_with_prompt = [ PromptRow.new('001:0: :> ', %q(a = 3..)), PromptRow.new('002:0: :* ', %q()), diff --git a/test/lib/jit_support.rb b/test/lib/jit_support.rb index 264fe9763e2a02..ce3ba6ba4b96b0 100644 --- a/test/lib/jit_support.rb +++ b/test/lib/jit_support.rb @@ -38,10 +38,10 @@ def eval_with_jit(env = nil, script, **opts) [stdout, stderr] end - def eval_with_jit_without_retry(env = nil, script, verbose: 0, min_calls: 5, save_temps: false, max_cache: 1000, wait: true, timeout: JIT_TIMEOUT) + def eval_with_jit_without_retry(env = nil, script, verbose: 0, call_threshold: 5, save_temps: false, max_cache: 1000, wait: true, timeout: JIT_TIMEOUT) args = [ '--disable-gems', "--mjit-verbose=#{verbose}", - "--mjit-min-calls=#{min_calls}", "--mjit-max-cache=#{max_cache}", + "--mjit-call-threshold=#{call_threshold}", "--mjit-max-cache=#{max_cache}", ] args << '--disable-yjit' args << '--mjit-wait' if wait diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 0932e93d5a909e..acf6722bb7391e 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -461,6 +461,30 @@ def test_until assert_not_equal(type1, type2) end + def test_rest_arg + rest_arg = lambda do |arg_str| + node = RubyVM::AbstractSyntaxTree.parse("def a(#{arg_str}) end") + node = node.children.last.children.last.children[1].children[-4] + end + + assert_equal(nil, rest_arg.call('')) + assert_equal(:r, rest_arg.call('*r')) + assert_equal(:r, rest_arg.call('a, *r')) + assert_equal(:*, rest_arg.call('*')) + assert_equal(:*, rest_arg.call('a, *')) + end + + def test_block_arg + block_arg = lambda do |arg_str| + node = RubyVM::AbstractSyntaxTree.parse("def a(#{arg_str}) end") + node = node.children.last.children.last.children[1].children[-1] + end + + assert_equal(nil, block_arg.call('')) + assert_equal(:block, block_arg.call('&block')) + assert_equal(:&, block_arg.call('&')) + end + def test_keyword_rest kwrest = lambda do |arg_str| node = RubyVM::AbstractSyntaxTree.parse("def a(#{arg_str}) end") @@ -469,7 +493,7 @@ def test_keyword_rest end assert_equal(nil, kwrest.call('')) - assert_equal([nil], kwrest.call('**')) + assert_equal([:**], kwrest.call('**')) assert_equal(false, kwrest.call('**nil')) assert_equal([:a], kwrest.call('**a')) end diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb index 8cd020533bd901..d0472a0081278b 100644 --- a/test/ruby/test_file_exhaustive.rb +++ b/test/ruby/test_file_exhaustive.rb @@ -649,7 +649,7 @@ def test_birthtime # ignore unsupporting filesystems rescue Errno::EPERM # Docker prohibits statx syscall by the default. - skip("statx(2) is prohibited by seccomp") + omit("statx(2) is prohibited by seccomp") end assert_raise(Errno::ENOENT) { File.birthtime(nofile) } end if File.respond_to?(:birthtime) diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 6313e111791977..0bf24960c6ee92 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -900,6 +900,10 @@ def test_copy_stream_socket3 end if defined? UNIXSocket def test_copy_stream_socket4 + if RUBY_PLATFORM =~ /mingw|mswin/ + omit "pread(2) is not implemented." + end + with_bigsrc {|bigsrc, bigcontent| File.open(bigsrc) {|f| assert_equal(0, f.pos) @@ -916,9 +920,13 @@ def test_copy_stream_socket4 } } } - end if defined? UNIXSocket + end def test_copy_stream_socket5 + if RUBY_PLATFORM =~ /mingw|mswin/ + omit "pread(2) is not implemented." + end + with_bigsrc {|bigsrc, bigcontent| File.open(bigsrc) {|f| assert_equal(bigcontent[0,100], f.read(100)) @@ -936,9 +944,13 @@ def test_copy_stream_socket5 } } } - end if defined? UNIXSocket + end def test_copy_stream_socket6 + if RUBY_PLATFORM =~ /mingw|mswin/ + omit "pread(2) is not implemented." + end + mkcdtmpdir { megacontent = "abc" * 1234567 File.open("megasrc", "w") {|f| f << megacontent } @@ -959,9 +971,13 @@ def test_copy_stream_socket6 assert_equal(megacontent, result) } } - end if defined? UNIXSocket + end def test_copy_stream_socket7 + if RUBY_PLATFORM =~ /mingw|mswin/ + omit "pread(2) is not implemented." + end + GC.start mkcdtmpdir { megacontent = "abc" * 1234567 @@ -996,7 +1012,7 @@ def test_copy_stream_socket7 end } } - end if defined? UNIXSocket and IO.method_defined?("nonblock=") + end def test_copy_stream_strio src = StringIO.new("abcd") diff --git a/test/ruby/test_io_timeout.rb b/test/ruby/test_io_timeout.rb index ca4c0b833b5b63..e017395980564b 100644 --- a/test/ruby/test_io_timeout.rb +++ b/test/ruby/test_io_timeout.rb @@ -9,12 +9,6 @@ def with_pipe begin i, o = UNIXSocket.pair - unless i.nonblock? && o.nonblock? - i.close - o.close - omit "I/O is not non-blocking!" - end - yield i, o ensure i.close diff --git a/test/ruby/test_mjit.rb b/test/ruby/test_mjit.rb index 2fc878154be639..2ed6d6ac091458 100644 --- a/test/ruby/test_mjit.rb +++ b/test/ruby/test_mjit.rb @@ -142,7 +142,7 @@ def test_compile_insn_instancevariable end; # optimized getinstancevariable call - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '33', success_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '33', success_count: 1, call_threshold: 2) begin; class A def initialize @@ -533,7 +533,7 @@ def test_compile_insn_opt_or def test_compile_insn_opt_aref # optimized call (optimized JIT) -> send call - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '21', success_count: 2, min_calls: 1, insns: %i[opt_aref]) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '21', success_count: 2, call_threshold: 1, insns: %i[opt_aref]) begin; obj = Object.new def obj.[](h) @@ -546,7 +546,7 @@ def obj.[](h) end; # send call -> optimized call (send JIT) -> optimized call - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '122', success_count: 2, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '122', success_count: 2, call_threshold: 2) begin; obj = Object.new def obj.[](h) @@ -637,7 +637,7 @@ def test(arg = 'hello') end def test_mjit_output - out, err = eval_with_jit('5.times { puts "MJIT" }', verbose: 1, min_calls: 5) + out, err = eval_with_jit('5.times { puts "MJIT" }', verbose: 1, call_threshold: 5) assert_equal("MJIT\n" * 5, out) assert_match(/^#{JIT_SUCCESS_PREFIX}: block in
@-e:1 -> .+_ruby_mjit_p\d+u\d+\.c$/, err) assert_match(/^Successful MJIT finish$/, err) @@ -686,7 +686,7 @@ def a11() print('hello') end def test_unload_units_and_compaction Dir.mktmpdir("jit_test_unload_units_") do |dir| # MIN_CACHE_SIZE is 10 - out, err = eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~'end;'}", verbose: 1, min_calls: 1, max_cache: 10) + out, err = eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~'end;'}", verbose: 1, call_threshold: 1, max_cache: 10) begin; i = 0 while i < 11 @@ -805,7 +805,7 @@ def wrapper(paths, prefixes) end def test_inlined_builtin_methods - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '', success_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '', success_count: 1, call_threshold: 2) begin; def test float = 0.0 @@ -819,7 +819,7 @@ def test end def test_inlined_c_method - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 2, recompile_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 2, recompile_count: 1, call_threshold: 2) begin; def test(obj, recursive: nil) if recursive @@ -837,7 +837,7 @@ def test(obj, recursive: nil) end def test_inlined_exivar - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 4, recompile_count: 2, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 4, recompile_count: 2, call_threshold: 2) begin; class Foo < Hash def initialize @@ -856,7 +856,7 @@ def bar end def test_inlined_undefined_ivar - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 2, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 2, call_threshold: 2) begin; class Foo def initialize @@ -877,7 +877,7 @@ def bar end def test_inlined_setivar_frozen - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "FrozenError\n", success_count: 2, min_calls: 3) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "FrozenError\n", success_count: 2, call_threshold: 3) begin; class A def a @@ -899,7 +899,7 @@ def a end def test_inlined_getconstant - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '11', success_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '11', success_count: 1, call_threshold: 2) begin; FOO = 1 def const @@ -911,7 +911,7 @@ def const end def test_attr_reader - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "4nil\nnil\n6", success_count: 2, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "4nil\nnil\n6", success_count: 2, call_threshold: 2) begin; class A attr_reader :a, :b @@ -942,7 +942,7 @@ def a.test print(2 * a.test) end; - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true", success_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true", success_count: 1, call_threshold: 2) begin; class Hoge attr_reader :foo @@ -977,7 +977,7 @@ def test(recv) def test_heap_promotion_of_ivar_in_the_middle_of_jit omit if GC.using_rvargc? - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true\ntrue\n", success_count: 2, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true\ntrue\n", success_count: 2, call_threshold: 2) begin; class A def initialize @@ -1004,7 +1004,7 @@ def add_ivar end def test_jump_to_precompiled_branch - assert_eval_with_jit("#{<<~'begin;'}\n#{<<~'end;'}", stdout: ".0", success_count: 1, min_calls: 1) + assert_eval_with_jit("#{<<~'begin;'}\n#{<<~'end;'}", stdout: ".0", success_count: 1, call_threshold: 1) begin; def test(foo) ".#{foo unless foo == 1}" if true @@ -1038,7 +1038,7 @@ def test_clean_objects_on_exec omit '.bundle.dSYM directory is left but removing it is not supported for now' end Dir.mktmpdir("jit_test_clean_objects_on_exec_") do |dir| - eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1) + eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~"end;"}", call_threshold: 1) begin; def a; end; a exec "true" @@ -1070,7 +1070,7 @@ def test_stack_pointer_with_assignment end def test_frame_omitted_inlining - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true\ntrue\ntrue\n", success_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true\ntrue\ntrue\n", success_count: 1, call_threshold: 2) begin; class Integer remove_method :zero? @@ -1086,7 +1086,7 @@ def zero? end def test_block_handler_with_possible_frame_omitted_inlining - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "70.0\n70.0\n70.0\n", success_count: 2, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "70.0\n70.0\n70.0\n", success_count: 2, call_threshold: 2) begin; def multiply(a, b) a *= b @@ -1099,7 +1099,7 @@ def multiply(a, b) end def test_builtin_frame_omitted_inlining - assert_eval_with_jit('0.zero?; 0.zero?; 3.times { p 0.zero? }', stdout: "true\ntrue\ntrue\n", success_count: 1, min_calls: 2) + assert_eval_with_jit('0.zero?; 0.zero?; 3.times { p 0.zero? }', stdout: "true\ntrue\ntrue\n", success_count: 1, call_threshold: 2) end def test_program_counter_with_regexpmatch @@ -1131,7 +1131,7 @@ def test_pushed_values_with_opt_aref_with end def test_mjit_pause_wait - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '', success_count: 0, min_calls: 1) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '', success_count: 0, call_threshold: 1) begin; RubyVM::MJIT.pause proc {}.call @@ -1139,7 +1139,7 @@ def test_mjit_pause_wait end def test_not_cancel_by_tracepoint_class - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 1, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 1, call_threshold: 2) begin; TracePoint.new(:class) {}.enable 2.times {} @@ -1147,7 +1147,7 @@ def test_not_cancel_by_tracepoint_class end def test_cancel_by_tracepoint - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 0, min_calls: 2) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 0, call_threshold: 2) begin; TracePoint.new(:line) {}.enable 2.times {} @@ -1155,7 +1155,7 @@ def test_cancel_by_tracepoint end def test_caller_locations_without_catch_table - out, _ = eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1) + out, _ = eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", call_threshold: 1) begin; def b # 2 caller_locations.first # 3 @@ -1175,8 +1175,8 @@ def a # 6 def test_fork_with_mjit_worker_thread Dir.mktmpdir("jit_test_fork_with_mjit_worker_thread_") do |dir| - # min_calls: 2 to skip fork block - out, err = eval_with_jit({ "TMPDIR" => dir }, "#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 2, verbose: 1) + # call_threshold: 2 to skip fork block + out, err = eval_with_jit({ "TMPDIR" => dir }, "#{<<~"begin;"}\n#{<<~"end;"}", call_threshold: 2, verbose: 1) begin; def before_fork; end def after_fork; end @@ -1206,7 +1206,7 @@ def after_fork; end end if defined?(fork) def test_jit_failure - _, err = eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1, verbose: 1) + _, err = eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", call_threshold: 1, verbose: 1) begin; 1.times do class A @@ -1230,14 +1230,14 @@ def assert_compile_once(script, result_inspect:, insns: [], uplevel: 1) end # Shorthand for normal test cases - def assert_eval_with_jit(script, stdout: nil, success_count:, recompile_count: nil, min_calls: 1, max_cache: 1000, insns: [], uplevel: 1, ignorable_patterns: []) - out, err = eval_with_jit(script, verbose: 1, min_calls: min_calls, max_cache: max_cache) + def assert_eval_with_jit(script, stdout: nil, success_count:, recompile_count: nil, call_threshold: 1, max_cache: 1000, insns: [], uplevel: 1, ignorable_patterns: []) + out, err = eval_with_jit(script, verbose: 1, call_threshold: call_threshold, max_cache: max_cache) success_actual = err.scan(/^#{JIT_SUCCESS_PREFIX}:/).size recompile_actual = err.scan(/^#{JIT_RECOMPILE_PREFIX}:/).size # Add --mjit-verbose=2 logs for cl.exe because compiler's error message is suppressed # for cl.exe with --mjit-verbose=1. See `start_process` in mjit.c. if RUBY_PLATFORM.match?(/mswin/) && success_count != success_actual - out2, err2 = eval_with_jit(script, verbose: 2, min_calls: min_calls, max_cache: max_cache) + out2, err2 = eval_with_jit(script, verbose: 2, call_threshold: call_threshold, max_cache: max_cache) end # Make sure that the script has insns expected to be tested diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 0198bbd9003e9f..15c9f293ede616 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -14,7 +14,7 @@ def self.yjit_enabled? = defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled if mjit_enabled? RUBY_DESCRIPTION.sub(/\+MJIT /, '') elsif yjit_enabled? - RUBY_DESCRIPTION.sub(/\+YJIT /, '') + RUBY_DESCRIPTION.sub(/\+YJIT( (dev|dev_nodebug|stats))? /, '') else RUBY_DESCRIPTION end diff --git a/test/ruby/test_rubyvm_mjit.rb b/test/ruby/test_rubyvm_mjit.rb index 8ca0fb9ef22cf1..90ff2edc8ef509 100644 --- a/test/ruby/test_rubyvm_mjit.rb +++ b/test/ruby/test_rubyvm_mjit.rb @@ -14,7 +14,7 @@ def setup end def test_pause - out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false) + out, err = eval_with_jit(<<~'EOS', verbose: 1, call_threshold: 1, wait: false) i = 0 while i < 5 eval("def mjit#{i}; end; mjit#{i}") @@ -36,7 +36,7 @@ def test_pause end def test_pause_waits_until_compaction - out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false) + out, err = eval_with_jit(<<~'EOS', verbose: 1, call_threshold: 1, wait: false) def a() end; a def b() end; b RubyVM::MJIT.pause @@ -52,7 +52,7 @@ def b() end; b end def test_pause_after_waitall - out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false) + out, err = eval_with_jit(<<~'EOS', verbose: 1, call_threshold: 1, wait: false) def test() = nil test Process.waitall @@ -65,7 +65,7 @@ def test() = nil end def test_pause_does_not_hang_on_full_units - out, _ = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, max_cache: 10, wait: false) + out, _ = eval_with_jit(<<~'EOS', verbose: 1, call_threshold: 1, max_cache: 10, wait: false) i = 0 while i < 11 eval("def mjit#{i}; end; mjit#{i}") @@ -77,7 +77,7 @@ def test_pause_does_not_hang_on_full_units end def test_pause_wait_false - out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false) + out, err = eval_with_jit(<<~'EOS', verbose: 1, call_threshold: 1, wait: false) i = 0 while i < 10 eval("def mjit#{i}; end; mjit#{i}") @@ -95,7 +95,7 @@ def test_pause_wait_false end def test_resume - out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false) + out, err = eval_with_jit(<<~'EOS', verbose: 1, call_threshold: 1, wait: false) print RubyVM::MJIT.resume print RubyVM::MJIT.pause print RubyVM::MJIT.resume diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 326ff3a4537391..848bb4971aadb4 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -88,8 +88,8 @@ def test_iv_index class TestObject; end - def test_new_obj_has_root_shape - assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(TestObject.new)) + def test_new_obj_has_t_object_shape + assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(TestObject.new).parent) end def test_str_has_root_shape @@ -114,14 +114,23 @@ def test_nil_has_special_const_shape_id def test_basic_shape_transition obj = Example.new - refute_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(obj)) - assert_shape_equal(RubyVM::Shape.root_shape.edges[:@a], RubyVM::Shape.of(obj)) + shape = RubyVM::Shape.of(obj) + refute_equal(RubyVM::Shape.root_shape, shape) + assert_equal :@a, shape.edge_name + assert_equal RubyVM::Shape::SHAPE_IVAR, shape.type + + shape = shape.parent + assert_equal RubyVM::Shape::SHAPE_T_OBJECT, shape.type + + shape = shape.parent + assert_equal(RubyVM::Shape.root_shape.id, shape.id) assert_equal(obj.instance_variable_get(:@a), 1) end def test_different_objects_make_same_transition - obj = Example.new + obj = [] obj2 = "" + obj.instance_variable_set(:@a, 1) obj2.instance_variable_set(:@a, 1) assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) end diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index b0ad012131208c..251448ec011615 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -163,9 +163,11 @@ def b(**); c(**) end def c(**kw); kw end def d(**); b(k: 1, **) end def e(**); b(**, k: 1) end + def f(a: nil, **); b(**) end assert_equal({a: 1, k: 3}, b(a: 1, k: 3)) assert_equal({a: 1, k: 3}, d(a: 1, k: 3)) assert_equal({a: 1, k: 1}, e(a: 1, k: 3)) + assert_equal({k: 3}, f(a: 1, k: 3)) end; end @@ -1707,6 +1709,8 @@ def test_argument_forwarding assert_syntax_error('def foo(...) foo[...] = x; end', /unexpected/) assert_syntax_error('def foo(...) foo(...) { }; end', /both block arg and actual block given/) assert_syntax_error('def foo(...) defined?(...); end', /unexpected/) + assert_syntax_error('def foo(*rest, ...) end', '... after rest argument') + assert_syntax_error('def foo(*, ...) end', '... after rest argument') obj1 = Object.new def obj1.bar(*args, **kws, &block) diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index fab8768a7e4692..9ab058d97bb2dd 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -905,7 +905,7 @@ def test_code_gc_with_many_iseqs end } - return :not_paged1 unless add_pages(500) # use some pages + return :not_paged1 unless add_pages(250) # use some pages return :broken_resume1 if fiber.resume != 0 # leave an on-stack code as well add_pages(2000) # use a whole lot of pages to run out of 1MiB diff --git a/test/socket/test_nonblock.rb b/test/socket/test_nonblock.rb index d9d1e186b2c518..5a4688bac3421a 100644 --- a/test/socket/test_nonblock.rb +++ b/test/socket/test_nonblock.rb @@ -307,11 +307,13 @@ def test_sendmsg_nonblock_seqpacket loop { s1.sendmsg_nonblock(buf) } end end - rescue NotImplementedError, Errno::ENOSYS, Errno::EPROTONOSUPPORT + rescue NotImplementedError, Errno::ENOSYS, Errno::EPROTONOSUPPORT, Errno::EPROTOTYPE omit "UNIXSocket.pair(:SEQPACKET) not implemented on this platform: #{$!}" end def test_sendmsg_nonblock_no_exception + omit "AF_UNIX + SEQPACKET is not supported on windows" if /mswin|mingw/ =~ RUBY_PLATFORM + buf = '*' * 4096 UNIXSocket.pair(:SEQPACKET) do |s1, s2| n = 0 diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb index 8c74d0c93989ea..e206339db09596 100644 --- a/test/socket/test_unix.rb +++ b/test/socket/test_unix.rb @@ -60,6 +60,8 @@ def test_fd_passing_class_mode assert_not_equal s1.fileno, r.fileno r.close end + rescue NotImplementedError => error + omit error.message end def test_fd_passing_n @@ -334,62 +336,70 @@ def test_cloexec end def test_noname_path - s1, s2 = UNIXSocket.pair - assert_equal("", s1.path) - assert_equal("", s2.path) - ensure - s1.close - s2.close + if /mswin|mingw/ =~ RUBY_PLATFORM + omit "unnamed pipe is emulated on windows" + end + + UNIXSocket.pair do |s1, s2| + assert_equal("", s1.path) + assert_equal("", s2.path) + end end def test_noname_addr - s1, s2 = UNIXSocket.pair - assert_equal(["AF_UNIX", ""], s1.addr) - assert_equal(["AF_UNIX", ""], s2.addr) - ensure - s1.close - s2.close + if /mswin|mingw/ =~ RUBY_PLATFORM + omit "unnamed pipe is emulated on windows" + end + + UNIXSocket.pair do |s1, s2| + assert_equal(["AF_UNIX", ""], s1.addr) + assert_equal(["AF_UNIX", ""], s2.addr) + end end def test_noname_peeraddr - s1, s2 = UNIXSocket.pair - assert_equal(["AF_UNIX", ""], s1.peeraddr) - assert_equal(["AF_UNIX", ""], s2.peeraddr) - ensure - s1.close - s2.close + if /mswin|mingw/ =~ RUBY_PLATFORM + omit "unnamed pipe is emulated on windows" + end + + UNIXSocket.pair do |s1, s2| + assert_equal(["AF_UNIX", ""], s1.peeraddr) + assert_equal(["AF_UNIX", ""], s2.peeraddr) + end end def test_noname_unpack_sockaddr_un - s1, s2 = UNIXSocket.pair - n = nil - assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != "" - assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != "" - assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getsockname) != "" - assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getpeername) != "" - assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getpeername) != "" - ensure - s1.close - s2.close + if /mswin|mingw/ =~ RUBY_PLATFORM + omit "unnamed pipe is emulated on windows" + end + + UNIXSocket.pair do |s1, s2| + n = nil + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != "" + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getsockname) != "" + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getsockname) != "" + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s1.getpeername) != "" + assert_equal("", Socket.unpack_sockaddr_un(n)) if (n = s2.getpeername) != "" + end end def test_noname_recvfrom - s1, s2 = UNIXSocket.pair - s2.write("a") - assert_equal(["a", ["AF_UNIX", ""]], s1.recvfrom(10)) - ensure - s1.close - s2.close + if /mswin|mingw/ =~ RUBY_PLATFORM + omit "unnamed pipe is emulated on windows" + end + + UNIXSocket.pair do |s1, s2| + s2.write("a") + assert_equal(["a", ["AF_UNIX", ""]], s1.recvfrom(10)) + end end def test_noname_recv_nonblock - s1, s2 = UNIXSocket.pair - s2.write("a") - IO.select [s1] - assert_equal("a", s1.recv_nonblock(10)) - ensure - s1.close - s2.close + UNIXSocket.pair do |s1, s2| + s2.write("a") + IO.select [s1] + assert_equal("a", s1.recv_nonblock(10)) + end end def test_too_long_path @@ -429,12 +439,18 @@ def test_dgram_pair rv = s1.recv(100, 0, buf) assert_equal buf.object_id, rv.object_id assert_equal "BBBBBB", rv + rescue Errno::EPROTOTYPE => error + omit error.message ensure s1.close if s1 s2.close if s2 end def test_dgram_pair_sendrecvmsg_errno_set + if /mswin|mingw/ =~ RUBY_PLATFORM + omit("AF_UNIX + SOCK_DGRAM is not supported on windows") + end + s1, s2 = to_close = UNIXSocket.pair(Socket::SOCK_DGRAM) pipe = IO.pipe to_close.concat(pipe) @@ -457,9 +473,17 @@ def test_dgram_pair_sendrecvmsg_errno_set end def test_epipe # [ruby-dev:34619] + # This is a good example of why reporting the exact `errno` is a terrible + # idea for platform abstractions. + if RUBY_PLATFORM =~ /mswin|mingw/ + error = Errno::ESHUTDOWN + else + error = Errno::EPIPE + end + UNIXSocket.pair {|s1, s2| s1.shutdown(Socket::SHUT_WR) - assert_raise(Errno::EPIPE) { s1.write "a" } + assert_raise(error) { s1.write "a" } assert_equal(nil, s2.read(1)) s2.write "a" assert_equal("a", s1.read(1)) @@ -493,6 +517,45 @@ def test_unix_socket_pair_close_on_exec } end + if /mingw|mswin/ =~ RUBY_PLATFORM + + def test_unix_socket_with_encoding + Dir.mktmpdir do |tmpdir| + path = "#{tmpdir}/sockäöü".encode("cp850") + UNIXServer.open(path) do |serv| + assert File.socket?(path) + assert File.stat(path).socket? + assert File.lstat(path).socket? + assert_equal path.encode("utf-8"), serv.path + UNIXSocket.open(path) do |s1| + s2 = serv.accept + s2.close + end + end + end + end + + def test_windows_unix_socket_pair_with_umlaut + otmp = ENV['TMP'] + ENV['TMP'] = File.join(Dir.tmpdir, "äöü€") + FileUtils.mkdir_p ENV['TMP'] + + s1, = UNIXSocket.pair + assert !s1.path.empty? + assert !File.exist?(s1.path) + ensure + FileUtils.rm_rf ENV['TMP'] + ENV['TMP'] = otmp + end + + def test_windows_unix_socket_pair_paths + s1, s2 = UNIXSocket.pair + assert !s1.path.empty? + assert s2.path.empty? + assert !File.exist?(s1.path) + end + end + def test_initialize Dir.mktmpdir {|d| Socket.open(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) {|s| diff --git a/thread.c b/thread.c index 89d4a910c14fa0..6e45b4d3154947 100644 --- a/thread.c +++ b/thread.c @@ -692,7 +692,7 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start) th->ec->machine.stack_maxsize -= size * sizeof(VALUE); // Ensure that we are not joinable. - VM_ASSERT(th->value == Qundef); + VM_ASSERT(UNDEF_P(th->value)); EC_PUSH_TAG(th->ec); @@ -738,7 +738,7 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start) } // The thread is effectively finished and can be joined. - VM_ASSERT(th->value != Qundef); + VM_ASSERT(!UNDEF_P(th->value)); rb_threadptr_join_list_wakeup(th); rb_threadptr_unlock_all_locking_mutexes(th); @@ -1038,7 +1038,7 @@ remove_from_join_list(VALUE arg) static int thread_finished(rb_thread_t *th) { - return th->status == THREAD_KILLED || th->value != Qundef; + return th->status == THREAD_KILLED || !UNDEF_P(th->value); } static VALUE @@ -1230,7 +1230,7 @@ thread_value(VALUE self) { rb_thread_t *th = rb_thread_ptr(self); thread_join(th, Qnil, 0); - if (th->value == Qundef) { + if (UNDEF_P(th->value)) { // If the thread is dead because we forked th->value is still Qundef. return Qnil; } @@ -2360,7 +2360,7 @@ rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing) RUBY_DEBUG_LOG("err:%"PRIdVALUE"\n", err); ret = TRUE; - if (err == Qundef) { + if (UNDEF_P(err)) { /* no error */ } else if (err == eKillSignal /* Thread#kill received */ || @@ -5046,7 +5046,7 @@ recursive_check(VALUE list, VALUE obj, VALUE paired_obj_id) #endif VALUE pair_list = rb_hash_lookup2(list, obj, Qundef); - if (pair_list == Qundef) + if (UNDEF_P(pair_list)) return Qfalse; if (paired_obj_id) { if (!RB_TYPE_P(pair_list, T_HASH)) { @@ -5078,7 +5078,7 @@ recursive_push(VALUE list, VALUE obj, VALUE paired_obj) if (!paired_obj) { rb_hash_aset(list, obj, Qtrue); } - else if ((pair_list = rb_hash_lookup2(list, obj, Qundef)) == Qundef) { + else if (UNDEF_P(pair_list = rb_hash_lookup2(list, obj, Qundef))) { rb_hash_aset(list, obj, paired_obj); } else { @@ -5105,7 +5105,7 @@ recursive_pop(VALUE list, VALUE obj, VALUE paired_obj) { if (paired_obj) { VALUE pair_list = rb_hash_lookup2(list, obj, Qundef); - if (pair_list == Qundef) { + if (UNDEF_P(pair_list)) { return 0; } if (RB_TYPE_P(pair_list, T_HASH)) { diff --git a/time.c b/time.c index fe4ea0014465ca..7d74b56a3be451 100644 --- a/time.c +++ b/time.c @@ -523,7 +523,7 @@ num_exact(VALUE v) return rb_rational_canonicalize(v); default: - if ((tmp = rb_check_funcall(v, idTo_r, 0, NULL)) != Qundef) { + if (!UNDEF_P(tmp = rb_check_funcall(v, idTo_r, 0, NULL))) { /* test to_int method availability to reject non-Numeric * objects such as String, Time, etc which have to_r method. */ if (!rb_respond_to(v, idTo_int)) { @@ -2277,7 +2277,7 @@ zone_set_dst(VALUE zone, struct time_object *tobj, VALUE tm) VALUE dst; CONST_ID(id_dst_p, "dst?"); dst = rb_check_funcall(zone, id_dst_p, 1, &tm); - tobj->vtm.isdst = (dst != Qundef && RTEST(dst)); + tobj->vtm.isdst = (!UNDEF_P(dst) && RTEST(dst)); } static int @@ -2290,7 +2290,7 @@ zone_timelocal(VALUE zone, VALUE time) t = rb_time_unmagnify(tobj->timew); tm = tm_from_time(rb_cTimeTM, time); utc = rb_check_funcall(zone, id_local_to_utc, 1, &tm); - if (utc == Qundef) return 0; + if (UNDEF_P(utc)) return 0; s = extract_time(utc); zone_set_offset(zone, tobj, t, s); @@ -2314,7 +2314,7 @@ zone_localtime(VALUE zone, VALUE time) tm = tm_from_time(rb_cTimeTM, time); local = rb_check_funcall(zone, id_utc_to_local, 1, &tm); - if (local == Qundef) return 0; + if (UNDEF_P(local)) return 0; s = extract_vtm(local, &tobj->vtm, subsecx); tobj->tm_got = 1; @@ -2616,7 +2616,7 @@ time_timespec(VALUE num, int interval) else { i = INT2FIX(1); ary = rb_check_funcall(num, id_divmod, 1, &i); - if (ary != Qundef && !NIL_P(ary = rb_check_array_type(ary))) { + if (!UNDEF_P(ary) && !NIL_P(ary = rb_check_array_type(ary))) { i = rb_ary_entry(ary, 0); f = rb_ary_entry(ary, 1); t.tv_sec = NUM2TIMET(i); @@ -5481,12 +5481,12 @@ rb_time_zone_abbreviation(VALUE zone, VALUE time) tm = tm_from_time(rb_cTimeTM, time); abbr = rb_check_funcall(zone, rb_intern("abbr"), 1, &tm); - if (abbr != Qundef) { + if (!UNDEF_P(abbr)) { goto found; } #ifdef SUPPORT_TZINFO_ZONE_ABBREVIATION abbr = rb_check_funcall(zone, rb_intern("period_for_utc"), 1, &tm); - if (abbr != Qundef) { + if (!UNDEF_P(abbr)) { abbr = rb_funcallv(abbr, rb_intern("abbreviation"), 0, 0); goto found; } @@ -5494,7 +5494,7 @@ rb_time_zone_abbreviation(VALUE zone, VALUE time) strftime_args[0] = rb_fstring_lit("%Z"); strftime_args[1] = tm; abbr = rb_check_funcall(zone, rb_intern("strftime"), 2, strftime_args); - if (abbr != Qundef) { + if (!UNDEF_P(abbr)) { goto found; } abbr = rb_check_funcall_default(zone, idName, 0, 0, Qnil); diff --git a/timev.rb b/timev.rb index a2a8bb3cb1577d..6bfb4e5e1b7729 100644 --- a/timev.rb +++ b/timev.rb @@ -330,12 +330,17 @@ def self.at(time, subsec = false, unit = :microsecond, in: nil) # Time.new(2000, 1, 1, 0, 0) # => 2000-01-01 00:00:00 -0600 # Time.new(2000, 1, 1, 0, 59) # => 2000-01-01 00:59:00 -0600 # - # - +sec+: Second in range (0..59), or 60 if +usec+ is zero: + # - +sec+: Second in range (0...61): # # Time.new(2000, 1, 1, 0, 0, 0) # => 2000-01-01 00:00:00 -0600 # Time.new(2000, 1, 1, 0, 0, 59) # => 2000-01-01 00:00:59 -0600 # Time.new(2000, 1, 1, 0, 0, 60) # => 2000-01-01 00:01:00 -0600 # + # +sec+ may be Float or Rational. + # + # Time.new(2000, 1, 1, 0, 0, 59.5) # => 2000-12-31 23:59:59.5 +0900 + # Time.new(2000, 1, 1, 0, 0, 59.7r) # => 2000-12-31 23:59:59.7 +0900 + # # These values may be: # # - Integers, as above. diff --git a/tool/generic_erb.rb b/tool/generic_erb.rb index 47bffd830c81dd..6607d5c256e458 100644 --- a/tool/generic_erb.rb +++ b/tool/generic_erb.rb @@ -23,6 +23,9 @@ output = out.path vpath = out.vpath +# A hack to prevent "unused variable" warnings +output, vpath = output, vpath + result = templates.map do |template| if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+ erb = ERB.new(File.read(template), trim_mode: '%-') diff --git a/tool/mjit/.gitignore b/tool/mjit/.gitignore deleted file mode 100644 index 66f8ed35a47c74..00000000000000 --- a/tool/mjit/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/Gemfile.lock diff --git a/tool/mjit/Gemfile b/tool/mjit/Gemfile deleted file mode 100644 index d7f2838765cd5b..00000000000000 --- a/tool/mjit/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source 'https://rubygems.org' - -gem 'ffi-clang', git: 'https://github.com/ioquatix/ffi-clang', ref: 'c08ca683c4f4cf73f4dc38539c3180288b360ea1' diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 5ffe7166bcd685..c1003fd723e4b4 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -1,8 +1,18 @@ #!/usr/bin/env ruby # frozen_string_literal: true -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('./Gemfile', __dir__) -require 'bundler/setup' +ENV['GEM_HOME'] = File.expand_path('./.bundle', __dir__) +require 'rubygems/source' +require 'bundler/inline' +gemfile do + source 'https://rubygems.org' + gem 'ffi-clang', '0.7.0', require: false +end + +# Help ffi-clang find libclang +# Hint: apt install libclang1 +ENV['LIBCLANG'] ||= Dir.glob("/usr/lib/llvm-*/lib/libclang.so.1").grep_v(/-cpp/).sort.last +require 'ffi/clang' require 'etc' require 'fiddle/import' @@ -12,11 +22,6 @@ abort "Usage: #{$0} BUILD_DIR" end -# Help ffi-clang find libclang -# Hint: apt install libclang1 -ENV['LIBCLANG'] ||= Dir.glob("/lib/#{RUBY_PLATFORM}-gnu/libclang-*.so*").grep_v(/-cpp/).sort.last -require 'ffi/clang' - class Node < Struct.new( :kind, :spelling, @@ -224,7 +229,7 @@ def generate_node(node, sizeof_type: nil) case child # BitField is struct-specific. So it must be handled here. - in Node[kind: :field_decl, spelling:, bitwidth:, children: [_grandchild]] if bitwidth > 0 + in Node[kind: :field_decl, spelling:, bitwidth:, children: [_grandchild, *]] if bitwidth > 0 buf << field_builder.call(spelling, "CType::BitField.new(#{bitwidth}, #{node.offsetof.fetch(spelling) % 8})") # "(unnamed ...)" struct and union are handled here, which are also struct-specific. in Node[kind: :field_decl, spelling:, type:, children: [grandchild]] if type.match?(/\((unnamed|anonymous) [^)]+\)\z/) @@ -341,7 +346,7 @@ def push_target(target) VM_METHOD_TYPE_ISEQ ], UINT: %w[ - SHAPE_BITS + SHAPE_ID_NUM_BITS SHAPE_CAPACITY_CHANGE SHAPE_FLAG_SHIFT SHAPE_FROZEN diff --git a/tool/test-bundled-gems.rb b/tool/test-bundled-gems.rb index 12358b69cc43ea..67de52c36c113c 100644 --- a/tool/test-bundled-gems.rb +++ b/tool/test-bundled-gems.rb @@ -35,11 +35,6 @@ test_command << " stdlib_test validate" first_timeout *= 3 - when "minitest" - # Tentatively exclude some tests that conflict with error_highlight - # https://github.com/seattlerb/minitest/pull/880 - test_command << " 'TESTOPTS=-e /test_stub_value_block_args_5__break_if_not_passed|test_no_method_error_on_unexpected_methods/'" - when "debug" # Since debug gem requires debug.so in child processes without # acitvating the gem, we preset necessary paths in RUBYLIB diff --git a/tool/update-deps b/tool/update-deps index ba94840f658117..f142b30be82bde 100755 --- a/tool/update-deps +++ b/tool/update-deps @@ -88,7 +88,6 @@ result.each {|k,v| # They can be referenced as $(top_srcdir)/filename. # % ruby -e 'def g(d) Dir.chdir(d) { Dir["**/*.{c,h,inc,dmyh}"] } end; puts((g("repo_source_dir_after_build") - g("repo_source_dir_original")).sort)' FILES_IN_SOURCE_DIRECTORY = %w[ - revision.h ] # Files built in the build directory (except extconf.h). @@ -129,6 +128,7 @@ FILES_NEED_VPATH = %w[ parse.c parse.h probes.dmyh + revision.h vm.inc vmtc.inc @@ -340,7 +340,7 @@ def read_single_cc_deps(path_i, cwd) next if /\A\# \d+ "(.*)"/ !~ line dep = $1 next if %r{\A<.*>\z} =~ dep # omit , etc. - next if /\.erb\z/ =~ dep + next if /\.e?rb\z/ =~ dep compiler_wd ||= dep files[dep] = true } diff --git a/transcode.c b/transcode.c index e315a8f1ab06b2..f1d871e292e794 100644 --- a/transcode.c +++ b/transcode.c @@ -2352,7 +2352,7 @@ transcode_loop(const unsigned char **in_pos, unsigned char **out_pos, ec->last_error.error_bytes_len, rb_enc_find(ec->last_error.source_encoding)); rep = (*fallback_func)(fallback, rep); - if (rep != Qundef && !NIL_P(rep)) { + if (!UNDEF_P(rep) && !NIL_P(rep)) { StringValue(rep); ret = rb_econv_insert_output(ec, (const unsigned char *)RSTRING_PTR(rep), RSTRING_LEN(rep), rb_enc_name(rb_enc_get(rep))); diff --git a/transient_heap.c b/transient_heap.c index ddf40d104153fc..9dfd980e64b95b 100644 --- a/transient_heap.c +++ b/transient_heap.c @@ -169,7 +169,7 @@ ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(static void transient_heap_ptr_check(struct static void transient_heap_ptr_check(struct transient_heap *theap, VALUE obj) { - if (obj != Qundef) { + if (!UNDEF_P(obj)) { const void *ptr = transient_heap_ptr(obj, FALSE); TH_ASSERT(ptr == NULL || transient_header_managed_ptr_p(theap, ptr)); } diff --git a/variable.c b/variable.c index 93abdac454752d..15e18f21aef084 100644 --- a/variable.c +++ b/variable.c @@ -281,7 +281,7 @@ rb_path_to_class(VALUE pathname) goto undefined_class; } c = rb_const_search(c, id, TRUE, FALSE, FALSE); - if (c == Qundef) goto undefined_class; + if (UNDEF_P(c)) goto undefined_class; if (!rb_namespace_p(c)) { rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module", pathname); @@ -1108,7 +1108,7 @@ gen_ivtbl_count(const struct gen_ivtbl *ivtbl) size_t n = 0; for (i = 0; i < ivtbl->numiv; i++) { - if (ivtbl->ivptr[i] != Qundef) { + if (!UNDEF_P(ivtbl->ivptr[i])) { n++; } } @@ -1598,7 +1598,7 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu break; } VALUE val = iv_list[shape->next_iv_index - 1]; - if (val != Qundef) { + if (!UNDEF_P(val)) { callback(shape->edge_name, val, itr_data->arg); } return; @@ -1606,6 +1606,7 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu case SHAPE_CAPACITY_CHANGE: case SHAPE_FROZEN: case SHAPE_IVAR_UNDEF: + case SHAPE_T_OBJECT: iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data); return; } @@ -1756,7 +1757,7 @@ rb_ivar_count(VALUE obj) st_index_t i, count, num = ROBJECT_IV_COUNT(obj); const VALUE *const ivptr = ROBJECT_IVPTR(obj); for (i = count = 0; i < num; ++i) { - if (ivptr[i] != Qundef) { + if (!UNDEF_P(ivptr[i])) { count++; } } @@ -1773,7 +1774,7 @@ rb_ivar_count(VALUE obj) st_index_t i, num = rb_shape_get_shape(obj)->next_iv_index; const VALUE *const ivptr = RCLASS_IVPTR(obj); for (i = count = 0; i < num; ++i) { - if (ivptr[i] != Qundef) { + if (!UNDEF_P(ivptr[i])) { count++; } } @@ -2283,7 +2284,7 @@ autoload_synchronized(VALUE _arguments) struct autoload_arguments *arguments = (struct autoload_arguments *)_arguments; rb_const_entry_t *constant_entry = rb_const_lookup(arguments->module, arguments->name); - if (constant_entry && constant_entry->value != Qundef) { + if (constant_entry && !UNDEF_P(constant_entry->value)) { return Qfalse; } @@ -2468,7 +2469,7 @@ autoloading_const_entry(VALUE mod, ID id) // Check if it's being loaded by the current thread/fiber: if (autoload_by_current(ele)) { - if (ac->value != Qundef) { + if (!UNDEF_P(ac->value)) { return ac; } } @@ -2482,7 +2483,7 @@ autoload_defined_p(VALUE mod, ID id) rb_const_entry_t *ce = rb_const_lookup(mod, id); // If there is no constant or the constant is not undefined (special marker for autoloading): - if (!ce || ce->value != Qundef) { + if (!ce || !UNDEF_P(ce->value)) { // We are not autoloading: return 0; } @@ -2578,7 +2579,7 @@ autoload_apply_constants(VALUE _arguments) // Iterate over all constants and assign them: ccan_list_for_each_safe(&arguments->autoload_data->constants, autoload_const, next, cnode) { - if (autoload_const->value != Qundef) { + if (!UNDEF_P(autoload_const->value)) { autoload_const_set(autoload_const); } } @@ -2617,7 +2618,7 @@ autoload_try_load(VALUE _arguments) // After we loaded the feature, if the constant is not defined, we remove it completely: rb_const_entry_t *ce = rb_const_lookup(arguments->module, arguments->name); - if (!ce || ce->value == Qundef) { + if (!ce || UNDEF_P(ce->value)) { result = Qfalse; rb_const_remove(arguments->module, arguments->name); @@ -2652,7 +2653,7 @@ rb_autoload_load(VALUE module, ID name) rb_const_entry_t *ce = rb_const_lookup(module, name); // We bail out as early as possible without any synchronisation: - if (!ce || ce->value != Qundef) { + if (!ce || !UNDEF_P(ce->value)) { return Qfalse; } @@ -2725,7 +2726,7 @@ static VALUE rb_const_get_0(VALUE klass, ID id, int exclude, int recurse, int visibility) { VALUE c = rb_const_search(klass, id, exclude, recurse, visibility); - if (c != Qundef) { + if (!UNDEF_P(c)) { if (UNLIKELY(!rb_ractor_main_p())) { if (!rb_ractor_shareable_p(c)) { rb_raise(rb_eRactorIsolationError, "can not access non-shareable objects in constant %"PRIsVALUE"::%s by non-main Ractor.", rb_class_path(klass), rb_id2name(id)); @@ -2769,7 +2770,7 @@ rb_const_search_from(VALUE klass, ID id, int exclude, int recurse, int visibilit } rb_const_warn_if_deprecated(ce, tmp, id); value = ce->value; - if (value == Qundef) { + if (UNDEF_P(value)) { struct autoload_const *ac; if (am == tmp) break; am = tmp; @@ -2798,7 +2799,7 @@ rb_const_search(VALUE klass, ID id, int exclude, int recurse, int visibility) if (klass == rb_cObject) exclude = FALSE; value = rb_const_search_from(klass, id, exclude, recurse, visibility); - if (value != Qundef) return value; + if (!UNDEF_P(value)) return value; if (exclude) return value; if (BUILTIN_TYPE(klass) != T_MODULE) return value; /* search global const too, if klass is a module */ @@ -2935,7 +2936,7 @@ rb_const_remove(VALUE mod, ID id) val = ce->value; - if (val == Qundef) { + if (UNDEF_P(val)) { autoload_delete(mod, id); val = Qnil; } @@ -3092,7 +3093,7 @@ rb_const_defined_0(VALUE klass, ID id, int exclude, int recurse, int visibility) if (visibility && RB_CONST_PRIVATE_P(ce)) { return (int)Qfalse; } - if (ce->value == Qundef && !check_autoload_required(tmp, id, 0) && + if (UNDEF_P(ce->value) && !check_autoload_required(tmp, id, 0) && !rb_autoloading_value(tmp, id, NULL, NULL)) return (int)Qfalse; @@ -3298,7 +3299,7 @@ const_tbl_update(struct autoload_const *ac, int autoload_force) if (rb_id_table_lookup(tbl, id, &value)) { ce = (rb_const_entry_t *)value; - if (ce->value == Qundef) { + if (UNDEF_P(ce->value)) { RUBY_ASSERT_CRITICAL_SECTION_ENTER(); VALUE file = ac->file; int line = ac->line; @@ -3398,7 +3399,7 @@ set_const_visibility(VALUE mod, int argc, const VALUE *argv, if ((ce = rb_const_lookup(mod, id))) { ce->flag &= ~mask; ce->flag |= flag; - if (ce->value == Qundef) { + if (UNDEF_P(ce->value)) { struct autoload_data *ele; ele = autoload_data_for_named_constant(mod, id, &ac); @@ -3508,7 +3509,7 @@ cvar_lookup_at(VALUE klass, ID id, st_data_t *v) } VALUE n = rb_ivar_lookup(klass, id, Qundef); - if (n == Qundef) return 0; + if (UNDEF_P(n)) return 0; if (v) *v = n; return 1; @@ -3838,7 +3839,7 @@ rb_mod_remove_cvar(VALUE mod, VALUE name) } rb_check_frozen(mod); val = rb_ivar_delete(mod, id, Qundef); - if (val != Qundef) { + if (!UNDEF_P(val)) { return (VALUE)val; } if (rb_cvar_defined(mod, id)) { diff --git a/vm.c b/vm.c index eff8a527a96468..d60db5d358c9ef 100644 --- a/vm.c +++ b/vm.c @@ -172,7 +172,7 @@ vm_ep_in_heap_p_(const rb_execution_context_t *ec, const VALUE *ep) if (VM_EP_IN_HEAP_P(ec, ep)) { VALUE envval = ep[VM_ENV_DATA_INDEX_ENV]; /* VM_ENV_ENVVAL(ep); */ - if (envval != Qundef) { + if (!UNDEF_P(envval)) { const rb_env_t *env = (const rb_env_t *)envval; VM_ASSERT(vm_assert_env(envval)); @@ -391,7 +391,7 @@ mjit_check_iseq(rb_execution_context_t *ec, const rb_iseq_t *iseq, struct rb_ise ASSUME(func_i <= LAST_JIT_ISEQ_FUNC); switch ((enum rb_mjit_iseq_func)func_i) { case NOT_ADDED_JIT_ISEQ_FUNC: - if (body->total_calls == mjit_opts.min_calls) { + if (body->total_calls == mjit_opts.call_threshold) { rb_mjit_add_iseq_to_process(iseq); if (UNLIKELY(mjit_opts.wait && (uintptr_t)body->jit_func > LAST_JIT_ISEQ_FUNC)) { return body->jit_func(ec, ec->cfp); @@ -1838,7 +1838,7 @@ rb_vm_make_jump_tag_but_local_jump(int state, VALUE val) default: return Qnil; } - if (val == Qundef) { + if (UNDEF_P(val)) { val = GET_EC()->tag->retval; } return make_localjump_error(mesg, val, state); @@ -2278,7 +2278,7 @@ vm_exec_enter_vm_loop(rb_execution_context_t *ec, struct rb_vm_exec_context *ctx ctx->result = ec->errinfo; rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY); - while ((ctx->result = vm_exec_handle_exception(ec, ctx->state, ctx->result, &ctx->initial)) == Qundef) { + while (UNDEF_P(ctx->result = vm_exec_handle_exception(ec, ctx->state, ctx->result, &ctx->initial))) { /* caught a jump, exec the handler */ ctx->result = vm_exec_core(ec, ctx->initial); vm_loop_start: @@ -2295,7 +2295,7 @@ vm_exec_bottom_main(void *context) struct rb_vm_exec_context *ctx = (struct rb_vm_exec_context *)context; ctx->state = TAG_NONE; - if (!ctx->jit_enable_p || (ctx->result = jit_exec(ctx->ec)) == Qundef) { + if (!ctx->jit_enable_p || UNDEF_P(ctx->result = jit_exec(ctx->ec))) { ctx->result = vm_exec_core(ctx->ec, ctx->initial); } vm_exec_enter_vm_loop(ctx->ec, ctx, ctx->tag, true); @@ -2347,7 +2347,7 @@ vm_exec(rb_execution_context_t *ec, bool jit_enable_p) _tag.retval = Qnil; if ((state = EC_EXEC_TAG()) == TAG_NONE) { - if (!jit_enable_p || (result = jit_exec(ec)) == Qundef) { + if (!jit_enable_p || UNDEF_P(result = jit_exec(ec))) { result = vm_exec_core(ec, initial); } goto vm_loop_start; /* fallback to the VM */ @@ -2355,7 +2355,7 @@ vm_exec(rb_execution_context_t *ec, bool jit_enable_p) else { result = ec->errinfo; rb_ec_raised_reset(ec, RAISED_STACKOVERFLOW | RAISED_NOMEMORY); - while ((result = vm_exec_handle_exception(ec, state, result, &initial)) == Qundef) { + while (UNDEF_P(result = vm_exec_handle_exception(ec, state, result, &initial))) { /* caught a jump, exec the handler */ result = vm_exec_core(ec, initial); vm_loop_start: diff --git a/vm_args.c b/vm_args.c index 35adfd94b183ec..0197109b032b67 100644 --- a/vm_args.c +++ b/vm_args.c @@ -283,7 +283,7 @@ make_unknown_kw_hash(const VALUE *passed_keywords, int passed_keyword_len, const VALUE obj = rb_ary_hidden_new(1); for (i=0; i 0; - args.respond_to_missing = (ret != Qundef); + args.respond_to_missing = !UNDEF_P(ret); ret = def; cme = callable_method_entry(klass, idMethodMissing, &args.defined_class); @@ -684,7 +684,7 @@ rb_check_funcall_default_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VAL if (!check_funcall_callable(ec, me)) { VALUE ret = check_funcall_missing(ec, klass, recv, mid, argc, argv, respond, def, kw_splat); - if (ret == Qundef) ret = def; + if (UNDEF_P(ret)) ret = def; return ret; } stack_check(ec); @@ -715,7 +715,7 @@ rb_check_funcall_with_hook_kw(VALUE recv, ID mid, int argc, const VALUE *argv, if (!check_funcall_callable(ec, me)) { VALUE ret = check_funcall_missing(ec, klass, recv, mid, argc, argv, respond, Qundef, kw_splat); - (*hook)(ret != Qundef, recv, mid, argc, argv, arg); + (*hook)(!UNDEF_P(ret), recv, mid, argc, argv, arg); return ret; } stack_check(ec); @@ -846,7 +846,7 @@ rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry defined_class = RBASIC(defined_class)->klass; } - if (self == Qundef || !rb_obj_is_kind_of(self, defined_class)) { + if (UNDEF_P(self) || !rb_obj_is_kind_of(self, defined_class)) { return MISSING_PROTECTED; } } @@ -1357,7 +1357,7 @@ rb_yield_1(VALUE val) VALUE rb_yield(VALUE val) { - if (val == Qundef) { + if (UNDEF_P(val)) { return rb_yield_0(0, NULL); } else { @@ -1698,7 +1698,7 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, fname = rb_source_location(&line); } - if (fname != Qundef) { + if (!UNDEF_P(fname)) { if (!NIL_P(fname)) fname = rb_fstring(fname); } else { diff --git a/vm_exec.c b/vm_exec.c index 0f59d08b963507..36bacfd5e84d8e 100644 --- a/vm_exec.c +++ b/vm_exec.c @@ -183,7 +183,7 @@ vm_exec_core(rb_execution_context_t *ec, VALUE initial) } } - if ((th = rb_ec_thread_ptr(ec))->retval != Qundef) { + if (!UNDEF_P((th = rb_ec_thread_ptr(ec))->retval)) { VALUE ret = th->retval; th->retval = Qundef; return ret; diff --git a/vm_insnhelper.c b/vm_insnhelper.c index a39b6f87ccc941..5d7d5ddee8f35e 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1018,7 +1018,7 @@ vm_get_ev_const(rb_execution_context_t *ec, VALUE orig_klass, ID id, bool allow_ if ((ce = rb_const_lookup(klass, id))) { rb_const_warn_if_deprecated(ce, klass, id); val = ce->value; - if (val == Qundef) { + if (UNDEF_P(val)) { if (am == klass) break; am = klass; if (is_defined) return 1; @@ -1212,7 +1212,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } val = ivar_list[index]; - RUBY_ASSERT(val != Qundef); + RUBY_ASSERT(!UNDEF_P(val)); } else { // cache miss case #if RUBY_DEBUG @@ -1243,7 +1243,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call // We fetched the ivar list above val = ivar_list[index]; - RUBY_ASSERT(val != Qundef); + RUBY_ASSERT(!UNDEF_P(val)); } else { if (is_attr) { @@ -1258,7 +1258,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } - RUBY_ASSERT(val != Qundef); + RUBY_ASSERT(!UNDEF_P(val)); return val; @@ -1517,7 +1517,7 @@ vm_getclassvariable(const rb_iseq_t *iseq, const rb_control_frame_t *reg_cfp, ID RB_DEBUG_COUNTER_INC(cvar_read_inline_hit); VALUE v = rb_ivar_lookup(ic->entry->class_value, id, Qundef); - RUBY_ASSERT(v != Qundef); + RUBY_ASSERT(!UNDEF_P(v)); return v; } @@ -1573,14 +1573,14 @@ vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IVC i attr_index_t index; vm_ic_atomic_shape_and_index(ic, &dest_shape_id, &index); - if (UNLIKELY(vm_setivar(obj, id, val, dest_shape_id, index) == Qundef)) { + if (UNLIKELY(UNDEF_P(vm_setivar(obj, id, val, dest_shape_id, index)))) { switch (BUILTIN_TYPE(obj)) { case T_OBJECT: case T_CLASS: case T_MODULE: break; default: - if (vm_setivar_default(obj, id, val, dest_shape_id, index) != Qundef) { + if (!UNDEF_P(vm_setivar_default(obj, id, val, dest_shape_id, index))) { return; } } @@ -2303,7 +2303,7 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd) VM_ASSERT(cd_owner != NULL); VALUE val = opt_equality_specialized(recv, obj); - if (val != Qundef) return val; + if (!UNDEF_P(val)) return val; if (!vm_method_cfunc_is(cd_owner, cd, recv, rb_obj_equal)) { return Qundef; @@ -2337,7 +2337,7 @@ static VALUE opt_equality_by_mid(VALUE recv, VALUE obj, ID mid) { VALUE val = opt_equality_specialized(recv, obj); - if (val != Qundef) { + if (!UNDEF_P(val)) { return val; } else { @@ -3301,7 +3301,7 @@ vm_call_attrset_direct(rb_execution_context_t *ec, rb_control_frame_t *cfp, cons ID id = vm_cc_cme(cc)->def->body.attr.id; rb_check_frozen_internal(obj); VALUE res = vm_setivar(obj, id, val, dest_shape_id, index); - if (res == Qundef) { + if (UNDEF_P(res)) { switch (BUILTIN_TYPE(obj)) { case T_OBJECT: case T_CLASS: @@ -3310,7 +3310,7 @@ vm_call_attrset_direct(rb_execution_context_t *ec, rb_control_frame_t *cfp, cons default: { res = vm_setivar_default(obj, id, val, dest_shape_id, index); - if (res != Qundef) { + if (!UNDEF_P(res)) { return res; } } @@ -4526,7 +4526,7 @@ check_respond_to_missing(VALUE obj, VALUE v) args[0] = obj; args[1] = Qfalse; r = rb_check_funcall(v, idRespond_to_missing, 2, args); - if (r != Qundef && RTEST(r)) { + if (!UNDEF_P(r) && RTEST(r)) { return true; } else { @@ -5076,7 +5076,7 @@ vm_sendish( } #endif - if (val != Qundef) { + if (!UNDEF_P(val)) { return val; /* CFUNC normal return */ } else { @@ -5094,7 +5094,7 @@ vm_sendish( VM_ENV_FLAGS_SET(GET_EP(), VM_FRAME_FLAG_FINISH); return vm_exec(ec, true); } - else if ((val = jit_exec(ec)) == Qundef) { + else if (UNDEF_P(val = jit_exec(ec))) { VM_ENV_FLAGS_SET(GET_EP(), VM_FRAME_FLAG_FINISH); return vm_exec(ec, false); } @@ -5550,7 +5550,7 @@ vm_opt_neq(const rb_iseq_t *iseq, CALL_DATA cd, CALL_DATA cd_eq, VALUE recv, VAL if (vm_method_cfunc_is(iseq, cd, recv, rb_obj_not_equal)) { VALUE val = opt_equality(iseq, recv, obj, cd_eq); - if (val != Qundef) { + if (!UNDEF_P(val)) { return RBOOL(!RTEST(val)); } } diff --git a/vm_method.c b/vm_method.c index 08e91ea0392f91..f1c50b9300ebc7 100644 --- a/vm_method.c +++ b/vm_method.c @@ -187,6 +187,7 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid) // invalidate CCs if (cc_tbl && rb_id_table_lookup(cc_tbl, mid, &ccs_data)) { struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_data; + rb_yjit_cme_invalidate((rb_callable_method_entry_t *)ccs->cme); if (NIL_P(ccs->cme->owner)) invalidate_negative_cache(mid); rb_vm_ccs_free(ccs); rb_id_table_delete(cc_tbl, mid); @@ -196,6 +197,10 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid) // remove from callable_m_tbl, if exists struct rb_id_table *cm_tbl; if ((cm_tbl = RCLASS_CALLABLE_M_TBL(klass)) != NULL) { + VALUE cme; + if (rb_yjit_enabled_p() && rb_id_table_lookup(cm_tbl, mid, &cme)) { + rb_yjit_cme_invalidate((rb_callable_method_entry_t *)cme); + } rb_id_table_delete(cm_tbl, mid); RB_DEBUG_COUNTER_INC(cc_invalidate_leaf_callable); } @@ -255,8 +260,6 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid) } } RB_VM_LOCK_LEAVE(); - - rb_yjit_method_lookup_change(klass, mid); } static void @@ -2735,7 +2738,7 @@ basic_obj_respond_to(rb_execution_context_t *ec, VALUE obj, ID id, int pub) case 0: ret = basic_obj_respond_to_missing(ec, klass, obj, ID2SYM(id), RBOOL(!pub)); - return RTEST(ret) && ret != Qundef; + return RTEST(ret) && !UNDEF_P(ret); default: return TRUE; } @@ -2847,7 +2850,7 @@ obj_respond_to(int argc, VALUE *argv, VALUE obj) if (!(id = rb_check_id(&mid))) { VALUE ret = basic_obj_respond_to_missing(ec, CLASS_OF(obj), obj, rb_to_symbol(mid), priv); - if (ret == Qundef) ret = Qfalse; + if (UNDEF_P(ret)) ret = Qfalse; return ret; } return RBOOL(basic_obj_respond_to(ec, obj, id, !RTEST(priv))); diff --git a/vm_trace.c b/vm_trace.c index 3e5cb29156c9f0..ea1d47361d624d 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -262,7 +262,7 @@ remove_event_hook(const rb_execution_context_t *ec, const rb_thread_t *filter_th while (hook) { if (func == 0 || hook->func == func) { if (hook->filter.th == filter_th || filter_th == MATCH_ANY_FILTER_TH) { - if (data == Qundef || hook->data == data) { + if (UNDEF_P(data) || hook->data == data) { hook->hook_flags |= RUBY_EVENT_HOOK_FLAG_DELETED; ret+=1; list->need_clean = true; @@ -857,7 +857,7 @@ rb_tracearg_event(rb_trace_arg_t *trace_arg) static void fill_path_and_lineno(rb_trace_arg_t *trace_arg) { - if (trace_arg->path == Qundef) { + if (UNDEF_P(trace_arg->path)) { get_path_and_lineno(trace_arg->ec, trace_arg->cfp, trace_arg->event, &trace_arg->path, &trace_arg->lineno); } } @@ -986,7 +986,7 @@ rb_tracearg_return_value(rb_trace_arg_t *trace_arg) else { rb_raise(rb_eRuntimeError, "not supported by this event"); } - if (trace_arg->data == Qundef) { + if (UNDEF_P(trace_arg->data)) { rb_bug("rb_tracearg_return_value: unreachable"); } return trace_arg->data; @@ -1001,7 +1001,7 @@ rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg) else { rb_raise(rb_eRuntimeError, "not supported by this event"); } - if (trace_arg->data == Qundef) { + if (UNDEF_P(trace_arg->data)) { rb_bug("rb_tracearg_raised_exception: unreachable"); } return trace_arg->data; @@ -1018,7 +1018,7 @@ rb_tracearg_eval_script(rb_trace_arg_t *trace_arg) else { rb_raise(rb_eRuntimeError, "not supported by this event"); } - if (data == Qundef) { + if (UNDEF_P(data)) { rb_bug("rb_tracearg_raised_exception: unreachable"); } if (rb_obj_is_iseq(data)) { @@ -1042,7 +1042,7 @@ rb_tracearg_instruction_sequence(rb_trace_arg_t *trace_arg) else { rb_raise(rb_eRuntimeError, "not supported by this event"); } - if (data == Qundef) { + if (UNDEF_P(data)) { rb_bug("rb_tracearg_raised_exception: unreachable"); } @@ -1067,7 +1067,7 @@ rb_tracearg_object(rb_trace_arg_t *trace_arg) else { rb_raise(rb_eRuntimeError, "not supported by this event"); } - if (trace_arg->data == Qundef) { + if (UNDEF_P(trace_arg->data)) { rb_bug("rb_tracearg_object: unreachable"); } return trace_arg->data; diff --git a/win32/Makefile.sub b/win32/Makefile.sub index e84f978bb72e4f..4b8904c5363b65 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -629,6 +629,9 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub #define HAVE_STDDEF_H 1 #define HAVE_STRING_H 1 #define HAVE_MEMORY_H 1 +!if $(MSC_VER) >= 1920 +#define HAVE_AFUNIX_H 1 +!endif !if $(MSC_VER) >= 1400 #define HAVE_LONG_LONG 1 !else diff --git a/win32/file.c b/win32/file.c index 243cb1722a0334..e047144d36245e 100644 --- a/win32/file.c +++ b/win32/file.c @@ -591,50 +591,6 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na return result; } -VALUE -rb_readlink(VALUE path, rb_encoding *resultenc) -{ - DWORD len; - VALUE wtmp = 0, wpathbuf, str; - rb_w32_reparse_buffer_t rbuf, *rp = &rbuf; - WCHAR *wpath, *wbuf; - rb_encoding *enc; - UINT cp, path_cp; - int e; - - FilePathValue(path); - enc = rb_enc_get(path); - cp = path_cp = code_page(enc); - if (cp == INVALID_CODE_PAGE) { - path = fix_string_encoding(path, enc); - cp = CP_UTF8; - } - len = MultiByteToWideChar(cp, 0, RSTRING_PTR(path), RSTRING_LEN(path), NULL, 0); - wpath = ALLOCV_N(WCHAR, wpathbuf, len+1); - MultiByteToWideChar(cp, 0, RSTRING_PTR(path), RSTRING_LEN(path), wpath, len); - wpath[len] = L'\0'; - e = rb_w32_read_reparse_point(wpath, rp, sizeof(rbuf), &wbuf, &len); - if (e == ERROR_MORE_DATA) { - size_t size = rb_w32_reparse_buffer_size(len + 1); - rp = ALLOCV(wtmp, size); - e = rb_w32_read_reparse_point(wpath, rp, size, &wbuf, &len); - } - ALLOCV_END(wpathbuf); - if (e) { - ALLOCV_END(wtmp); - if (e != -1) - rb_syserr_fail_path(rb_w32_map_errno(e), path); - else /* not symlink; maybe volume mount point */ - rb_syserr_fail_path(EINVAL, path); - } - enc = resultenc; - path_cp = code_page(enc); - len = lstrlenW(wbuf); - str = append_wstr(rb_enc_str_new(0, 0, enc), wbuf, len, path_cp, enc); - ALLOCV_END(wtmp); - return str; -} - int rb_file_load_ok(const char *path) { diff --git a/win32/file.h b/win32/file.h index 36ff27c9b1c744..4f1f36a75cd72a 100644 --- a/win32/file.h +++ b/win32/file.h @@ -1,10 +1,12 @@ #ifndef RUBY_WIN32_FILE_H #define RUBY_WIN32_FILE_H -#define MAX_REPARSE_PATH_LEN 4092 +#ifndef IO_REPARSE_TAG_AF_UNIX +# define IO_REPARSE_TAG_AF_UNIX 0x80000023 +#endif enum { - MINIMUM_REPARSE_BUFFER_PATH_LEN = 4 + MINIMUM_REPARSE_BUFFER_PATH_LEN = 100 }; /* License: Ruby's */ typedef struct { @@ -18,14 +20,14 @@ typedef struct { USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; - WCHAR PathBuffer[4]; + WCHAR PathBuffer[MINIMUM_REPARSE_BUFFER_PATH_LEN]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; - WCHAR PathBuffer[4]; + WCHAR PathBuffer[MINIMUM_REPARSE_BUFFER_PATH_LEN]; } MountPointReparseBuffer; }; } rb_w32_reparse_buffer_t; diff --git a/win32/win32.c b/win32/win32.c index 865edc8aa68f41..fee31677f0b229 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -49,6 +49,9 @@ #ifdef __MINGW32__ #include #endif +#ifdef HAVE_AFUNIX_H +# include +#endif #include "ruby/win32.h" #include "ruby/vm.h" #include "win32/dir.h" @@ -4018,15 +4021,93 @@ rb_w32_getservbyport(int port, const char *proto) return r; } +#ifdef HAVE_AFUNIX_H + +/* License: Ruby's */ +static size_t +socketpair_unix_path(struct sockaddr_un *sock_un) +{ + SOCKET listener; + WCHAR wpath[sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path)] = L""; + + /* AF_UNIX/SOCK_STREAM became available in Windows 10 + * See https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows + */ + listener = socket(AF_UNIX, SOCK_STREAM, 0); + if (listener == INVALID_SOCKET) + return 0; + + memset(sock_un, 0, sizeof(*sock_un)); + sock_un->sun_family = AF_UNIX; + + /* Abstract sockets (filesystem-independent) don't work, contrary to + * the claims of the aforementioned blog post: + * https://github.com/microsoft/WSL/issues/4240#issuecomment-549663217 + * + * So we must use a named path, and that comes with all the attendant + * problems of permissions and collisions. Trying various temporary + * directories and putting high-res time and PID in the filename. + */ + for (int try = 0; ; try++) { + LARGE_INTEGER ticks; + size_t path_len = 0; + const size_t maxpath = sizeof(sock_un->sun_path)/sizeof(*sock_un->sun_path); + + switch (try) { + case 0: + /* user temp dir from TMP or TEMP env var, it ends with a backslash */ + path_len = GetTempPathW(maxpath, wpath); + break; + case 1: + wcsncpy(wpath, L"C:/Temp/", maxpath); + path_len = lstrlenW(wpath); + break; + case 2: + /* Current directory */ + path_len = 0; + break; + case 3: + closesocket(listener); + return 0; + } + + /* Windows UNIXSocket implementation expects UTF-8 instead of UTF16 */ + path_len = WideCharToMultiByte(CP_UTF8, 0, wpath, path_len, sock_un->sun_path, maxpath, NULL, NULL); + QueryPerformanceCounter(&ticks); + path_len += snprintf(sock_un->sun_path + path_len, + maxpath - path_len, + "%lld-%ld.($)", + ticks.QuadPart, + GetCurrentProcessId()); + + /* Convert to UTF16 for DeleteFileW */ + MultiByteToWideChar(CP_UTF8, 0, sock_un->sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath)); + + if (bind(listener, (struct sockaddr *)sock_un, sizeof(*sock_un)) != SOCKET_ERROR) + break; + } + closesocket(listener); + DeleteFileW(wpath); + return sizeof(*sock_un); +} +#endif + /* License: Ruby's */ static int socketpair_internal(int af, int type, int protocol, SOCKET *sv) { SOCKET svr = INVALID_SOCKET, r = INVALID_SOCKET, w = INVALID_SOCKET; struct sockaddr_in sock_in4; + #ifdef INET6 struct sockaddr_in6 sock_in6; #endif + +#ifdef HAVE_AFUNIX_H + struct sockaddr_un sock_un = {0, {0}}; + WCHAR wpath[sizeof(sock_un.sun_path)/sizeof(*sock_un.sun_path)] = L""; +#endif + struct sockaddr *addr; int ret = -1; int len; @@ -4050,6 +4131,15 @@ socketpair_internal(int af, int type, int protocol, SOCKET *sv) addr = (struct sockaddr *)&sock_in6; len = sizeof(sock_in6); break; +#endif +#ifdef HAVE_AFUNIX_H + case AF_UNIX: + addr = (struct sockaddr *)&sock_un; + len = socketpair_unix_path(&sock_un); + MultiByteToWideChar(CP_UTF8, 0, sock_un.sun_path, -1, wpath, sizeof(wpath)/sizeof(*wpath)); + if (len) + break; + /* fall through */ #endif default: errno = EAFNOSUPPORT; @@ -4101,6 +4191,10 @@ socketpair_internal(int af, int type, int protocol, SOCKET *sv) } if (svr != INVALID_SOCKET) closesocket(svr); +#ifdef HAVE_AFUNIX_H + if (sock_un.sun_family == AF_UNIX) + DeleteFileW(wpath); +#endif } return ret; @@ -5144,32 +5238,35 @@ rb_w32_read_reparse_point(const WCHAR *path, rb_w32_reparse_buffer_t *rp, static ssize_t w32_readlink(UINT cp, const char *path, char *buf, size_t bufsize) { - VALUE wtmp; + VALUE rp_buf, rp_buf_bigger = 0; DWORD len = MultiByteToWideChar(cp, 0, path, -1, NULL, 0); - size_t size = rb_w32_reparse_buffer_size(len); - WCHAR *wname, *wpath = ALLOCV(wtmp, size + sizeof(WCHAR) * len); + size_t size = rb_w32_reparse_buffer_size(bufsize); + WCHAR *wname; + WCHAR *wpath = ALLOCV(rp_buf, sizeof(WCHAR) * len + size); rb_w32_reparse_buffer_t *rp = (void *)(wpath + len); ssize_t ret; int e; MultiByteToWideChar(cp, 0, path, -1, wpath, len); e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len); - if (e && e != ERROR_MORE_DATA) { - ALLOCV_END(wtmp); - errno = map_errno(e); + if (e == ERROR_MORE_DATA) { + size = rb_w32_reparse_buffer_size(len + 1); + rp = ALLOCV(rp_buf_bigger, size); + e = rb_w32_read_reparse_point(wpath, rp, size, &wname, &len); + } + if (e) { + ALLOCV_END(rp_buf); + ALLOCV_END(rp_buf_bigger); + errno = e == -1 ? EINVAL : map_errno(e); return -1; } - len = lstrlenW(wname) + 1; + len = lstrlenW(wname); ret = WideCharToMultiByte(cp, 0, wname, len, buf, bufsize, NULL, NULL); - ALLOCV_END(wtmp); - if (e) { + ALLOCV_END(rp_buf); + ALLOCV_END(rp_buf_bigger); + if (!ret) { ret = bufsize; } - else if (!ret) { - e = GetLastError(); - errno = map_errno(e); - ret = -1; - } return ret; } @@ -5629,10 +5726,8 @@ fileattr_to_unixmode(DWORD attr, const WCHAR *path, unsigned mode) /* format is already set */ } else if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { - if (rb_w32_reparse_symlink_p(path)) - mode |= S_IFLNK | S_IEXEC; - else - mode |= S_IFDIR | S_IEXEC; + /* Only used by stat_by_find in the case the file can not be opened. + * In this case we can't get more details. */ } else if (attr & FILE_ATTRIBUTE_DIRECTORY) { mode |= S_IFDIR | S_IEXEC; @@ -5707,14 +5802,6 @@ stat_by_find(const WCHAR *path, struct stati128 *st) { HANDLE h; WIN32_FIND_DATAW wfd; - /* GetFileAttributesEx failed; check why. */ - int e = GetLastError(); - - if ((e == ERROR_FILE_NOT_FOUND) || (e == ERROR_INVALID_NAME) - || (e == ERROR_PATH_NOT_FOUND || (e == ERROR_BAD_NETPATH))) { - errno = map_errno(e); - return -1; - } /* Fall back to FindFirstFile for ERROR_SHARING_VIOLATION */ h = FindFirstFileW(path, &wfd); @@ -5750,9 +5837,24 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat) DWORD flags = lstat ? FILE_FLAG_OPEN_REPARSE_POINT : 0; HANDLE f; WCHAR finalname[PATH_MAX]; + int open_error; memset(st, 0, sizeof(*st)); f = open_special(path, 0, flags); + open_error = GetLastError(); + if (f == INVALID_HANDLE_VALUE && !lstat) { + /* Support stat (not only lstat) of UNIXSocket */ + FILE_ATTRIBUTE_TAG_INFO attr_info; + DWORD e; + + f = open_special(path, 0, FILE_FLAG_OPEN_REPARSE_POINT); + e = GetFileInformationByHandleEx( f, FileAttributeTagInfo, + &attr_info, sizeof(attr_info)); + if (!e || attr_info.ReparseTag != IO_REPARSE_TAG_AF_UNIX) { + CloseHandle(f); + f = INVALID_HANDLE_VALUE; + } + } if (f != INVALID_HANDLE_VALUE) { DWORD attr = stati128_handle(f, st); const DWORD len = get_final_path(f, finalname, numberof(finalname), 0); @@ -5764,15 +5866,26 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat) case FILE_TYPE_PIPE: mode = S_IFIFO; break; + default: + if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { + FILE_ATTRIBUTE_TAG_INFO attr_info; + DWORD e; + + e = GetFileInformationByHandleEx( f, FileAttributeTagInfo, + &attr_info, sizeof(attr_info)); + if (e && attr_info.ReparseTag == IO_REPARSE_TAG_AF_UNIX) { + st->st_size = 0; + mode |= S_IFSOCK; + } else if (rb_w32_reparse_symlink_p(path)) { + /* TODO: size in which encoding? */ + st->st_size = 0; + mode |= S_IFLNK | S_IEXEC; + } else { + mode |= S_IFDIR | S_IEXEC; + } + } } CloseHandle(f); - if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { - /* TODO: size in which encoding? */ - if (rb_w32_reparse_symlink_p(path)) - st->st_size = 0; - else - attr &= ~FILE_ATTRIBUTE_REPARSE_POINT; - } if (attr & FILE_ATTRIBUTE_DIRECTORY) { if (check_valid_dir(path)) return -1; } @@ -5785,6 +5898,12 @@ winnt_stat(const WCHAR *path, struct stati128 *st, BOOL lstat) } } else { + if ((open_error == ERROR_FILE_NOT_FOUND) || (open_error == ERROR_INVALID_NAME) + || (open_error == ERROR_PATH_NOT_FOUND || (open_error == ERROR_BAD_NETPATH))) { + errno = map_errno(open_error); + return -1; + } + if (stat_by_find(path, st)) return -1; } diff --git a/yjit.c b/yjit.c index aa49b3cfdca4f6..db0a5f5978a265 100644 --- a/yjit.c +++ b/yjit.c @@ -1071,6 +1071,14 @@ rb_yjit_invalidate_all_method_lookup_assumptions(void) // method caches, so we do nothing here for now. } +// Number of object shapes, which might be useful for investigating YJIT exit reasons. +static VALUE +object_shape_count(rb_execution_context_t *ec, VALUE self) +{ + // next_shape_id starts from 0, so it's the same as the count + return ULONG2NUM((unsigned long)GET_VM()->next_shape_id); +} + // Primitives used by yjit.rb VALUE rb_yjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self); VALUE rb_yjit_trace_exit_locations_enabled_p(rb_execution_context_t *ec, VALUE self); diff --git a/yjit.h b/yjit.h index 5f2722639f3946..707c0f4b9d92eb 100644 --- a/yjit.h +++ b/yjit.h @@ -28,7 +28,6 @@ bool rb_yjit_enabled_p(void); unsigned rb_yjit_call_threshold(void); void rb_yjit_invalidate_all_method_lookup_assumptions(void); -void rb_yjit_method_lookup_change(VALUE klass, ID mid); void rb_yjit_cme_invalidate(rb_callable_method_entry_t *cme); void rb_yjit_collect_vm_usage_insn(int insn); void rb_yjit_collect_binding_alloc(void); @@ -51,7 +50,6 @@ void rb_yjit_tracing_invalidate_all(void); static inline bool rb_yjit_enabled_p(void) { return false; } static inline unsigned rb_yjit_call_threshold(void) { return UINT_MAX; } static inline void rb_yjit_invalidate_all_method_lookup_assumptions(void) {} -static inline void rb_yjit_method_lookup_change(VALUE klass, ID mid) {} static inline void rb_yjit_cme_invalidate(rb_callable_method_entry_t *cme) {} static inline void rb_yjit_collect_vm_usage_insn(int insn) {} static inline void rb_yjit_collect_binding_alloc(void) {} diff --git a/yjit.rb b/yjit.rb index cac2430127881c..8d67069b1365e9 100644 --- a/yjit.rb +++ b/yjit.rb @@ -146,7 +146,10 @@ def self.dump_exit_locations(filename) # Return nil when option is not passed or unavailable. def self.runtime_stats stats = Primitive.rb_yjit_get_stats - return stats if stats.nil? || !Primitive.rb_yjit_stats_enabled_p + return stats if stats.nil? + + stats[:object_shape_count] = Primitive.object_shape_count + return stats unless Primitive.rb_yjit_stats_enabled_p side_exits = total_exit_count(stats) total_exits = side_exits + stats[:leave_interp_return] @@ -163,6 +166,7 @@ def self.runtime_stats # Proportion of instructions that retire in YJIT total_insns_count = retired_in_yjit + stats[:vm_insns_count] yjit_ratio_pct = 100.0 * retired_in_yjit.to_f / total_insns_count + stats[:total_insns_count] = total_insns_count stats[:ratio_in_yjit] = yjit_ratio_pct end @@ -254,22 +258,25 @@ def _print_stats $stderr.puts "bindings_allocations: " + ("%10d" % stats[:binding_allocations]) $stderr.puts "bindings_set: " + ("%10d" % stats[:binding_set]) $stderr.puts "compilation_failure: " + ("%10d" % compilation_failure) if compilation_failure != 0 - $stderr.puts "compiled_block_count: " + ("%10d" % stats[:compiled_block_count]) $stderr.puts "compiled_iseq_count: " + ("%10d" % stats[:compiled_iseq_count]) + $stderr.puts "compiled_block_count: " + ("%10d" % stats[:compiled_block_count]) + $stderr.puts "compiled_branch_count: " + ("%10d" % stats[:compiled_branch_count]) $stderr.puts "freed_iseq_count: " + ("%10d" % stats[:freed_iseq_count]) $stderr.puts "invalidation_count: " + ("%10d" % stats[:invalidation_count]) $stderr.puts "constant_state_bumps: " + ("%10d" % stats[:constant_state_bumps]) $stderr.puts "inline_code_size: " + ("%10d" % stats[:inline_code_size]) $stderr.puts "outlined_code_size: " + ("%10d" % stats[:outlined_code_size]) $stderr.puts "freed_code_size: " + ("%10d" % stats[:freed_code_size]) + $stderr.puts "code_region_size: " + ("%10d" % stats[:code_region_size]) $stderr.puts "yjit_alloc_size: " + ("%10d" % stats[:yjit_alloc_size]) if stats.key?(:yjit_alloc_size) $stderr.puts "live_page_count: " + ("%10d" % stats[:live_page_count]) $stderr.puts "freed_page_count: " + ("%10d" % stats[:freed_page_count]) $stderr.puts "code_gc_count: " + ("%10d" % stats[:code_gc_count]) $stderr.puts "num_gc_obj_refs: " + ("%10d" % stats[:num_gc_obj_refs]) + $stderr.puts "object_shape_count: " + ("%10d" % stats[:object_shape_count]) $stderr.puts "side_exit_count: " + ("%10d" % stats[:side_exit_count]) - $stderr.puts "total_exit_count: " + ("%10d" % stats[:side_exit_count]) - $stderr.puts "total_insns_count: " + ("%10d" % stats[:total_exit_count]) + $stderr.puts "total_exit_count: " + ("%10d" % stats[:total_exit_count]) + $stderr.puts "total_insns_count: " + ("%10d" % stats[:total_insns_count]) if stats.key?(:total_insns_count) if stats.key?(:vm_insns_count) $stderr.puts "vm_insns_count: " + ("%10d" % stats[:vm_insns_count]) end diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 2b94d95608de6c..9afba864d247b0 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -85,7 +85,7 @@ fn main() { // From shape.h .allowlist_function("rb_shape_get_shape_id") .allowlist_function("rb_shape_get_shape_by_id") - .allowlist_function("rb_shape_flags_mask") + .allowlist_function("rb_shape_id_num_bits") .allowlist_function("rb_shape_get_iv_index") // From ruby/internal/intern/object.h diff --git a/yjit/src/asm/arm64/mod.rs b/yjit/src/asm/arm64/mod.rs index 683711259e89c2..d4636139438f5f 100644 --- a/yjit/src/asm/arm64/mod.rs +++ b/yjit/src/asm/arm64/mod.rs @@ -540,8 +540,6 @@ pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { pub fn ldurh(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) { let bytes: [u8; 4] = match (rt, rn) { (A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => { - assert!(rt.num_bits == 32, "Rt should be a 32 bit register"); - assert!(rn.num_bits == 64, "Rn should be a 64 bit register"); assert!(mem_disp_fits_bits(rn.disp), "Expected displacement to be 9 bits or less"); LoadStore::ldurh(rt.reg_no, rn.base_reg_no, rn.disp as i16).into() diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs index 4f3d20f5e5f772..728444b281e868 100644 --- a/yjit/src/asm/mod.rs +++ b/yjit/src/asm/mod.rs @@ -210,12 +210,15 @@ impl CodeBlock { self.page_size } + pub fn mapped_region_size(&self) -> usize { + self.mem_block.borrow().mapped_region_size() + } + /// Return the number of code pages that have been mapped by the VirtualMemory. pub fn num_mapped_pages(&self) -> usize { - let mapped_region_size = self.mem_block.borrow().mapped_region_size(); // CodeBlock's page size != VirtualMem's page size on Linux, // so mapped_region_size % self.page_size may not be 0 - ((mapped_region_size - 1) / self.page_size) + 1 + ((self.mapped_region_size() - 1) / self.page_size) + 1 } /// Return the number of code pages that have been reserved by the VirtualMemory. diff --git a/yjit/src/asm/x86_64/mod.rs b/yjit/src/asm/x86_64/mod.rs index 616a54ba7bd129..30fe4072b4b7d5 100644 --- a/yjit/src/asm/x86_64/mod.rs +++ b/yjit/src/asm/x86_64/mod.rs @@ -1034,6 +1034,20 @@ pub fn mov(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) { }; } +/// A variant of mov used for always writing the value in 64 bits for GC offsets. +pub fn movabs(cb: &mut CodeBlock, dst: X86Opnd, value: u64) { + match dst { + X86Opnd::Reg(reg) => { + assert_eq!(reg.num_bits, 64); + write_rex(cb, true, 0, 0, reg.reg_no); + + write_opcode(cb, 0xb8, reg); + cb.write_int(value, 64); + }, + _ => unreachable!() + } +} + /// movsx - Move with sign extension (signed integers) pub fn movsx(cb: &mut CodeBlock, dst: X86Opnd, src: X86Opnd) { if let X86Opnd::Reg(_dst_reg) = dst { diff --git a/yjit/src/asm/x86_64/tests.rs b/yjit/src/asm/x86_64/tests.rs index 57cc0807102e62..c6d49e084d2e47 100644 --- a/yjit/src/asm/x86_64/tests.rs +++ b/yjit/src/asm/x86_64/tests.rs @@ -188,6 +188,12 @@ fn test_mov() { check_bytes("48c742f8f4ffffff", |cb| mov(cb, mem_opnd(64, RDX, -8), imm_opnd(-12))); } +#[test] +fn test_movabs() { + check_bytes("49b83400000000000000", |cb| movabs(cb, R8, 0x34)); + check_bytes("49b80000008000000000", |cb| movabs(cb, R8, 0x80000000)); +} + #[test] fn test_mov_unsigned() { // MOV AL, imm8 diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index c899f6871f9d1e..cbda5eec054ffa 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -344,7 +344,14 @@ impl Assembler Insn::Cmp { left, right } => { let opnd0 = split_load_operand(asm, left); let opnd0 = split_less_than_32_cmp(asm, opnd0); - let opnd1 = split_shifted_immediate(asm, right); + let split_right = split_shifted_immediate(asm, right); + let opnd1 = match split_right { + Opnd::InsnOut { .. } if opnd0.num_bits() != split_right.num_bits() => { + split_right.with_num_bits(opnd0.num_bits().unwrap()).unwrap() + }, + _ => split_right + }; + asm.cmp(opnd0, opnd1); }, Insn::CRet(opnd) => { @@ -828,6 +835,7 @@ impl Assembler Opnd::Mem(_) => { match opnd.rm_num_bits() { 64 | 32 => ldur(cb, out.into(), opnd.into()), + 16 => ldurh(cb, out.into(), opnd.into()), 8 => ldurb(cb, out.into(), opnd.into()), num_bits => panic!("unexpected num_bits: {}", num_bits) }; @@ -1356,6 +1364,15 @@ mod tests { assert_eq!(4, cb.get_write_pos()); } + #[test] + fn test_32_bit_register_with_some_number() { + let (mut asm, mut cb) = setup_asm(); + + let shape_opnd = Opnd::mem(32, Opnd::Reg(X0_REG), 6); + asm.cmp(shape_opnd, Opnd::UImm(4097)); + asm.compile_with_num_regs(&mut cb, 2); + } + #[test] fn test_emit_xor() { let (mut asm, mut cb) = setup_asm(); diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 0aa087630d5d42..973fe89b6d0c22 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -109,8 +109,8 @@ impl Opnd }) }, - Opnd::InsnOut{idx, num_bits } => { - assert!(num_bits == 64); + Opnd::InsnOut{idx, num_bits: out_num_bits } => { + assert!(num_bits <= out_num_bits); Opnd::Mem(Mem { base: MemBase::InsnOut(idx), disp: disp, @@ -143,7 +143,7 @@ impl Opnd } /// Get the size in bits for this operand if there is one. - fn num_bits(&self) -> Option { + pub fn num_bits(&self) -> Option { match *self { Opnd::Reg(Reg { num_bits, .. }) => Some(num_bits), Opnd::Mem(Mem { num_bits, .. }) => Some(num_bits), diff --git a/yjit/src/backend/x86_64/mod.rs b/yjit/src/backend/x86_64/mod.rs index dc5f21221da048..68cd35574fd1a6 100644 --- a/yjit/src/backend/x86_64/mod.rs +++ b/yjit/src/backend/x86_64/mod.rs @@ -465,15 +465,15 @@ impl Assembler // This assumes only load instructions can contain references to GC'd Value operands Insn::Load { opnd, out } | Insn::LoadInto { dest: out, opnd } => { - mov(cb, out.into(), opnd.into()); - - // If the value being loaded is a heap object - if let Opnd::Value(val) = opnd { - if !val.special_const_p() { + match opnd { + Opnd::Value(val) if val.heap_object_p() => { + // Using movabs because mov might write value in 32 bits + movabs(cb, out.into(), val.0 as _); // The pointer immediate is encoded as the last part of the mov written out let ptr_offset: u32 = (cb.get_write_pos() as u32) - (SIZEOF_VALUE as u32); insn_gc_offsets.push(ptr_offset); } + _ => mov(cb, out.into(), opnd.into()) } }, diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index ac70cf98bda24b..c66a29332228cd 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -721,7 +721,8 @@ pub fn gen_single_block( #[cfg(feature = "disasm")] if get_option_ref!(dump_disasm).is_some() { - asm.comment(&format!("Block: {} (ISEQ offset: {})", iseq_get_location(blockid.iseq), blockid.idx)); + let blockid_idx = blockid.idx; + asm.comment(&format!("Block: {} (ISEQ offset: {})", iseq_get_location(blockid.iseq), blockid_idx)); } // For each instruction to compile @@ -829,9 +830,7 @@ pub fn gen_single_block( let gc_offsets = asm.compile(cb); // Add the GC offsets to the block - for offset in gc_offsets { - block.add_gc_obj_offset(offset) - } + block.add_gc_obj_offsets(gc_offsets); // Mark the end position of the block block.set_end_addr(cb.get_write_ptr()); @@ -2041,16 +2040,14 @@ fn gen_get_ivar( // Compile time self is embedded and the ivar index lands within the object let embed_test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) }; - let flags_mask: usize = unsafe { rb_shape_flags_mask() }.as_usize(); - let expected_flags_mask: usize = (RUBY_T_MASK as usize) | !flags_mask | (ROBJECT_EMBED as usize); - let expected_flags = comptime_receiver.builtin_flags() & expected_flags_mask; - - // Combined guard for all flags: shape, embeddedness, and T_OBJECT - let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); + let expected_shape = unsafe { rb_shape_get_shape_id(comptime_receiver) }; + let shape_bit_size = unsafe { rb_shape_id_num_bits() }; // either 16 or 32 depending on RUBY_DEBUG + let shape_byte_size = shape_bit_size / 8; + let shape_opnd = Opnd::mem(shape_bit_size, recv, RUBY_OFFSET_RBASIC_FLAGS + (8 - shape_byte_size as i32)); - asm.comment("guard shape, embedded, and T_OBJECT"); - let flags_opnd = asm.and(flags_opnd, Opnd::UImm(expected_flags_mask as u64)); - asm.cmp(flags_opnd, Opnd::UImm(expected_flags as u64)); + asm.comment("guard shape"); + asm.cmp(shape_opnd, Opnd::UImm(expected_shape as u64)); + let megamorphic_side_exit = counted_exit!(ocb, side_exit, getivar_megamorphic).into(); jit_chain_guard( JCC_JNE, jit, @@ -2058,7 +2055,7 @@ fn gen_get_ivar( asm, ocb, max_chain_depth, - side_exit, + megamorphic_side_exit, ); match ivar_index { @@ -3895,7 +3892,7 @@ fn jit_obj_respond_to( // Invalidate this block if method lookup changes for the method being queried. This works // both for the case where a method does or does not exist, as for the latter we asked for a // "negative CME" earlier. - assume_method_lookup_stable(jit, ocb, recv_class, target_cme); + assume_method_lookup_stable(jit, ocb, target_cme); // Generate a side exit let side_exit = get_side_exit(jit, ocb, ctx); @@ -4179,7 +4176,7 @@ fn gen_send_cfunc( } // Delegate to codegen for C methods if we have it. - if kw_arg.is_null() { + if kw_arg.is_null() && flags & VM_CALL_OPT_SEND == 0 { let codegen_p = lookup_cfunc_codegen(unsafe { (*cme).def }); if let Some(known_cfunc_codegen) = codegen_p { if known_cfunc_codegen(jit, ctx, asm, ocb, ci, cme, block, argc, recv_known_klass) { @@ -5291,7 +5288,7 @@ fn gen_send_general( // Register block for invalidation //assert!(cme->called_id == mid); - assume_method_lookup_stable(jit, ocb, comptime_recv_klass, cme); + assume_method_lookup_stable(jit, ocb, cme); // To handle the aliased method case (VM_METHOD_TYPE_ALIAS) loop { @@ -5470,7 +5467,7 @@ fn gen_send_general( flags |= VM_CALL_FCALL | VM_CALL_OPT_SEND; - assume_method_lookup_stable(jit, ocb, comptime_recv_klass, cme); + assume_method_lookup_stable(jit, ocb, cme); let (known_class, type_mismatch_exit) = { if compile_time_name.string_p() { @@ -5891,8 +5888,8 @@ fn gen_invokesuper( // We need to assume that both our current method entry and the super // method entry we invoke remain stable - assume_method_lookup_stable(jit, ocb, current_defined_class, me); - assume_method_lookup_stable(jit, ocb, comptime_superclass, cme); + assume_method_lookup_stable(jit, ocb, me); + assume_method_lookup_stable(jit, ocb, cme); // Method calls may corrupt types ctx.clear_local_types(); diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 0dcaa73453e539..869c0859b5e869 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -304,6 +304,7 @@ pub struct Context { /// Tuple of (iseq, idx) used to identify basic blocks /// There are a lot of blockid objects so we try to keep the size small. #[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[repr(packed)] pub struct BlockId { /// Instruction sequence pub iseq: IseqPtr, @@ -369,13 +370,8 @@ impl Branch { } } -// In case this block is invalidated, these two pieces of info -// help to remove all pointers to this block in the system. -#[derive(Debug)] -pub struct CmeDependency { - pub receiver_klass: VALUE, - pub callee_cme: *const rb_callable_method_entry_t, -} +// In case a block is invalidated, this helps to remove all pointers to the block. +pub type CmePtr = *const rb_callable_method_entry_t; /// Basic block version /// Represents a portion of an iseq compiled with a given context @@ -412,7 +408,7 @@ pub struct Block { // CME dependencies of this block, to help to remove all pointers to this // block in the system. - cme_dependencies: Vec, + cme_dependencies: Vec, // Code address of an exit for `ctx` and `blockid`. // Used for block invalidation. @@ -635,9 +631,8 @@ pub extern "C" fn rb_yjit_iseq_mark(payload: *mut c_void) { unsafe { rb_gc_mark_movable(block.blockid.iseq.into()) }; // Mark method entry dependencies - for cme_dep in &block.cme_dependencies { - unsafe { rb_gc_mark_movable(cme_dep.receiver_klass) }; - unsafe { rb_gc_mark_movable(cme_dep.callee_cme.into()) }; + for &cme_dep in &block.cme_dependencies { + unsafe { rb_gc_mark_movable(cme_dep.into()) }; } // Mark outgoing branch entries @@ -692,8 +687,7 @@ pub extern "C" fn rb_yjit_iseq_update_references(payload: *mut c_void) { // Update method entry dependencies for cme_dep in &mut block.cme_dependencies { - cme_dep.receiver_klass = unsafe { rb_gc_location(cme_dep.receiver_klass) }; - cme_dep.callee_cme = unsafe { rb_gc_location(cme_dep.callee_cme.into()) }.as_cme(); + *cme_dep = unsafe { rb_gc_location((*cme_dep).into()) }.as_cme(); } // Update outgoing branch entries @@ -884,13 +878,13 @@ fn add_block_version(blockref: &BlockRef, cb: &CodeBlock) { let version_list = get_or_create_version_list(block.blockid); version_list.push(blockref.clone()); + version_list.shrink_to_fit(); // By writing the new block to the iseq, the iseq now // contains new references to Ruby objects. Run write barriers. let iseq: VALUE = block.blockid.iseq.into(); - for dep in block.iter_cme_deps() { - obj_written!(iseq, dep.receiver_klass); - obj_written!(iseq, dep.callee_cme.into()); + for &dep in block.iter_cme_deps() { + obj_written!(iseq, dep.into()); } // Run write barriers for all objects in generated code. @@ -972,7 +966,7 @@ impl Block { } /// Get an immutable iterator over cme dependencies - pub fn iter_cme_deps(&self) -> std::slice::Iter<'_, CmeDependency> { + pub fn iter_cme_deps(&self) -> std::slice::Iter<'_, CmePtr> { self.cme_dependencies.iter() } @@ -1000,22 +994,31 @@ impl Block { self.end_idx = end_idx; } - pub fn add_gc_obj_offset(self: &mut Block, ptr_offset: u32) { - self.gc_obj_offsets.push(ptr_offset); - incr_counter!(num_gc_obj_refs); + pub fn add_gc_obj_offsets(self: &mut Block, gc_offsets: Vec) { + for offset in gc_offsets { + self.gc_obj_offsets.push(offset); + incr_counter!(num_gc_obj_refs); + } + self.gc_obj_offsets.shrink_to_fit(); } /// Instantiate a new CmeDependency struct and add it to the list of /// dependencies for this block. - pub fn add_cme_dependency( - &mut self, - receiver_klass: VALUE, - callee_cme: *const rb_callable_method_entry_t, - ) { - self.cme_dependencies.push(CmeDependency { - receiver_klass, - callee_cme, - }); + pub fn add_cme_dependency(&mut self, callee_cme: CmePtr) { + self.cme_dependencies.push(callee_cme); + self.cme_dependencies.shrink_to_fit(); + } + + // Push an incoming branch ref and shrink the vector + fn push_incoming(&mut self, branch: BranchRef) { + self.incoming.push(branch); + self.incoming.shrink_to_fit(); + } + + // Push an outgoing branch ref and shrink the vector + fn push_outgoing(&mut self, branch: BranchRef) { + self.outgoing.push(branch); + self.outgoing.shrink_to_fit(); } // Compute the size of the block code @@ -1508,8 +1511,7 @@ fn gen_block_series_body( last_branch.dst_addrs[0] = new_blockref.borrow().start_addr; new_blockref .borrow_mut() - .incoming - .push(last_branchref.clone()); + .push_incoming(last_branchref.clone()); // Track the block batch.push(new_blockref.clone()); @@ -1526,7 +1528,8 @@ fn gen_block_series_body( let iseq_location = iseq_get_location(blockid.iseq); if iseq_location.contains(substr) { let last_block = last_blockref.borrow(); - println!("Compiling {} block(s) for {}, ISEQ offsets [{}, {})", batch.len(), iseq_location, blockid.idx, last_block.end_idx); + let blockid_idx = blockid.idx; + println!("Compiling {} block(s) for {}, ISEQ offsets [{}, {})", batch.len(), iseq_location, blockid_idx, last_block.end_idx); print!("{}", disasm_iseq_insn_range(blockid.iseq, blockid.idx, last_block.end_idx)); } } @@ -1668,7 +1671,8 @@ fn make_branch_entry(block: &BlockRef, gen_fn: BranchGenFn) -> BranchRef { // Add to the list of outgoing branches for the block let branchref = Rc::new(RefCell::new(branch)); - block.borrow_mut().outgoing.push(branchref.clone()); + block.borrow_mut().push_outgoing(branchref.clone()); + incr_counter!(compiled_branch_count); return branchref; } @@ -1799,7 +1803,7 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) - assert!(!(branch.shape == target_branch_shape && block.start_addr != branch.end_addr)); // Add this branch to the list of incoming branches for the target - block.incoming.push(branch_rc.clone()); + block.push_incoming(branch_rc.clone()); // Update the branch target address let dst_addr = block.start_addr; @@ -1847,8 +1851,8 @@ fn branch_stub_hit_body(branch_ptr: *const c_void, target_idx: u32, ec: EcPtr) - let new_branch_size = branch.code_size(); assert!( new_branch_size <= branch_size_on_entry, - "branch stubs should never enlarge branches: (old_size: {}, new_size: {})", - branch_size_on_entry, new_branch_size, + "branch stubs should never enlarge branches (start_addr: {:?}, old_size: {}, new_size: {})", + branch.start_addr.unwrap().raw_ptr(), branch_size_on_entry, new_branch_size, ); // Return a pointer to the compiled block version @@ -1870,7 +1874,7 @@ fn get_branch_target( let mut block = blockref.borrow_mut(); // Add an incoming branch into this block - block.incoming.push(branchref.clone()); + block.push_incoming(branchref.clone()); let mut branch = branchref.borrow_mut(); branch.blocks[target_idx.as_usize()] = Some(blockref.clone()); @@ -2017,7 +2021,7 @@ pub fn gen_direct_jump(jit: &JITState, ctx: &Context, target0: BlockId, asm: &mu if let Some(blockref) = maybe_block { let mut block = blockref.borrow_mut(); - block.incoming.push(branchref.clone()); + block.push_incoming(branchref.clone()); branch.dst_addrs[0] = block.start_addr; branch.blocks[0] = Some(blockref.clone()); @@ -2147,7 +2151,8 @@ pub fn invalidate_block_version(blockref: &BlockRef) { if let Some(substr) = get_option_ref!(dump_iseq_disasm).as_ref() { let iseq_location = iseq_get_location(block.blockid.iseq); if iseq_location.contains(substr) { - println!("Invalidating block from {}, ISEQ offsets [{}, {})", iseq_location, block.blockid.idx, block.end_idx); + let blockid_idx = block.blockid.idx; + println!("Invalidating block from {}, ISEQ offsets [{}, {})", iseq_location, blockid_idx, block.end_idx); } } } @@ -2253,14 +2258,17 @@ pub fn invalidate_block_version(blockref: &BlockRef) { } // Rewrite the branch with the new jump target address - let branch_end_addr = branch.end_addr; + let old_branch_size = branch.code_size(); regenerate_branch(cb, &mut branch); if target_next && branch.end_addr > block.end_addr { panic!("yjit invalidate rewrote branch past end of invalidated block: {:?} (code_size: {})", branch, block.code_size()); } - if !target_next && branch.end_addr > branch_end_addr { - panic!("invalidated branch grew in size: {:?}", branch); + if !target_next && branch.code_size() > old_branch_size { + panic!( + "invalidated branch grew in size (start_addr: {:?}, old_size: {}, new_size: {})", + branch.start_addr.unwrap().raw_ptr(), old_branch_size, branch.code_size() + ); } } diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 168443e6f0a8a6..d37af383207b30 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -333,6 +333,11 @@ impl VALUE { self.immediate_p() || !self.test() } + /// Return true if the value is a heap object + pub fn heap_object_p(self) -> bool { + !self.special_const_p() + } + /// Return true if the value is a Ruby Fixnum (immediate-size integer) pub fn fixnum_p(self) -> bool { let VALUE(cval) = self; diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 17e5a84c3c6c60..78967cd4ced2ab 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -424,6 +424,9 @@ pub struct rb_shape { pub parent_id: shape_id_t, } pub type rb_shape_t = rb_shape; +extern "C" { + pub fn rb_shape_id_num_bits() -> u8; +} extern "C" { pub fn rb_shape_get_shape_by_id(shape_id: shape_id_t) -> *mut rb_shape_t; } @@ -433,9 +436,6 @@ extern "C" { extern "C" { pub fn rb_shape_get_iv_index(shape: *mut rb_shape_t, id: ID, value: *mut attr_index_t) -> bool; } -extern "C" { - pub fn rb_shape_flags_mask() -> VALUE; -} pub const idDot2: ruby_method_ids = 128; pub const idDot3: ruby_method_ids = 129; pub const idUPlus: ruby_method_ids = 132; diff --git a/yjit/src/disasm.rs b/yjit/src/disasm.rs index e958f3bfce0d1c..bab0133c4d949c 100644 --- a/yjit/src/disasm.rs +++ b/yjit/src/disasm.rs @@ -90,11 +90,12 @@ pub fn disasm_iseq_insn_range(iseq: IseqPtr, start_idx: u32, end_idx: u32) -> St let code_size = block.code_size(); // Write some info about the current block + let blockid_idx = blockid.idx; let block_ident = format!( "BLOCK {}/{}, ISEQ RANGE [{},{}), {} bytes ", block_idx + 1, block_list.len(), - blockid.idx, + blockid_idx, end_idx, code_size ); diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs index cd3214feae546d..b911ff2c921a5f 100644 --- a/yjit/src/invariants.rs +++ b/yjit/src/invariants.rs @@ -25,11 +25,6 @@ pub struct Invariants { /// Tracks block assumptions about callable method entry validity. cme_validity: HashMap<*const rb_callable_method_entry_t, HashSet>, - /// Tracks block assumptions about method lookup. Maps a class to a table of - /// method ID points to a set of blocks. While a block `b` is in the table, - /// b->callee_cme == rb_callable_method_entry(klass, mid). - method_lookup: HashMap>>, - /// A map from a class and its associated basic operator to a set of blocks /// that are assuming that that operator is not redefined. This is used for /// quick access to all of the blocks that are making this assumption when @@ -68,7 +63,6 @@ impl Invariants { unsafe { INVARIANTS = Some(Invariants { cme_validity: HashMap::new(), - method_lookup: HashMap::new(), basic_operator_blocks: HashMap::new(), block_basic_operators: HashMap::new(), single_ractor: HashSet::new(), @@ -124,34 +118,20 @@ pub fn assume_bop_not_redefined( pub fn assume_method_lookup_stable( jit: &mut JITState, ocb: &mut OutlinedCb, - receiver_klass: VALUE, callee_cme: *const rb_callable_method_entry_t, ) { - // RUBY_ASSERT(rb_callable_method_entry(receiver_klass, cme->called_id) == cme); - // RUBY_ASSERT_ALWAYS(RB_TYPE_P(receiver_klass, T_CLASS) || RB_TYPE_P(receiver_klass, T_ICLASS)); - // RUBY_ASSERT_ALWAYS(!rb_objspace_garbage_object_p(receiver_klass)); - jit_ensure_block_entry_exit(jit, ocb); let block = jit.get_block(); block .borrow_mut() - .add_cme_dependency(receiver_klass, callee_cme); + .add_cme_dependency(callee_cme); Invariants::get_instance() .cme_validity .entry(callee_cme) .or_default() .insert(block.clone()); - - let mid = unsafe { (*callee_cme).called_id }; - Invariants::get_instance() - .method_lookup - .entry(receiver_klass) - .or_default() - .entry(mid) - .or_default() - .insert(block); } // Checks rb_method_basic_definition_p and registers the current block for invalidation if method @@ -166,7 +146,7 @@ pub fn assume_method_basic_definition( ) -> bool { if unsafe { rb_method_basic_definition_p(klass, mid) } != 0 { let cme = unsafe { rb_callable_method_entry(klass, mid) }; - assume_method_lookup_stable(jit, ocb, klass, cme); + assume_method_lookup_stable(jit, ocb, cme); true } else { false @@ -272,31 +252,6 @@ pub extern "C" fn rb_yjit_cme_invalidate(callee_cme: *const rb_callable_method_e }); } -/// Callback for when rb_callable_method_entry(klass, mid) is going to change. -/// Invalidate blocks that assume stable method lookup of `mid` in `klass` when this happens. -/// This needs to be wrapped on the C side with RB_VM_LOCK_ENTER(). -#[no_mangle] -pub extern "C" fn rb_yjit_method_lookup_change(klass: VALUE, mid: ID) { - // If YJIT isn't enabled, do nothing - if !yjit_enabled_p() { - return; - } - - with_vm_lock(src_loc!(), || { - Invariants::get_instance() - .method_lookup - .entry(klass) - .and_modify(|deps| { - if let Some(deps) = deps.remove(&mid) { - for block in &deps { - invalidate_block_version(block); - incr_counter!(invalidate_method_lookup); - } - } - }); - }); -} - /// Callback for then Ruby is about to spawn a ractor. In that case we need to /// invalidate every block that is assuming single ractor mode. #[no_mangle] @@ -387,16 +342,6 @@ pub extern "C" fn rb_yjit_root_mark() { unsafe { rb_gc_mark(cme) }; } - - // Mark class and iclass objects - for klass in invariants.method_lookup.keys() { - // TODO: This is a leak. Unused blocks linger in the table forever, preventing the - // callee class they speculate on from being collected. - // We could do a bespoke weak reference scheme on classes similar to - // the interpreter's call cache. See finalizer for T_CLASS and cc_table_free(). - - unsafe { rb_gc_mark(*klass) }; - } } /// Remove all invariant assumptions made by the block by removing the block as @@ -410,17 +355,9 @@ pub fn block_assumptions_free(blockref: &BlockRef) { // For each method lookup dependency for dep in block.iter_cme_deps() { // Remove tracking for cme validity - if let Some(blockset) = invariants.cme_validity.get_mut(&dep.callee_cme) { + if let Some(blockset) = invariants.cme_validity.get_mut(dep) { blockset.remove(blockref); } - - // Remove tracking for lookup stability - if let Some(id_to_block_set) = invariants.method_lookup.get_mut(&dep.receiver_klass) { - let mid = unsafe { (*dep.callee_cme).called_id }; - if let Some(block_set) = id_to_block_set.get_mut(&mid) { - block_set.remove(&blockref); - } - } } } diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index cfce4c9c33f2b6..03eec21b50bc42 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -267,6 +267,7 @@ make_counters! { vm_insns_count, compiled_iseq_count, compiled_block_count, + compiled_branch_count, compilation_failure, freed_iseq_count, @@ -402,6 +403,9 @@ fn rb_yjit_gen_stats_dict() -> VALUE { // Code GC count hash_aset_usize!(hash, "code_gc_count", CodegenGlobals::get_code_gc_count()); + // Size of memory region allocated for JIT code + hash_aset_usize!(hash, "code_region_size", cb.mapped_region_size()); + // Rust global allocations in bytes #[cfg(feature="stats")] hash_aset_usize!(hash, "yjit_alloc_size", global_allocation_size()); diff --git a/yjit/src/virtualmem.rs b/yjit/src/virtualmem.rs index 1d80983c9e9e44..668e3d6a7b84d5 100644 --- a/yjit/src/virtualmem.rs +++ b/yjit/src/virtualmem.rs @@ -59,7 +59,7 @@ pub trait Allocator { /// We may later change this to wrap an u32. /// Note: there is no NULL constant for CodePtr. You should use Option instead. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] -#[repr(C)] +#[repr(C, packed)] pub struct CodePtr(*const u8); /// Errors that can happen when writing to [VirtualMemory]