From d15d1c01c28f63888475f8016adbc8b4f982b573 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 14 Nov 2022 23:38:52 -0800 Subject: [PATCH 01/77] Rename --mjit-min-calls to --mjit-call-threshold (#6731) for consistency with YJIT --- NEWS.md | 1 + benchmark/lib/benchmark_driver/runner/mjit.rb | 2 +- bootstraptest/test_ractor.rb | 2 +- mjit.c | 26 ++++----- mjit.h | 2 +- mjit_c.rb | 2 +- test/lib/jit_support.rb | 4 +- test/ruby/test_mjit.rb | 58 +++++++++---------- test/ruby/test_rubyvm_mjit.rb | 12 ++-- vm.c | 2 +- 10 files changed, 56 insertions(+), 55 deletions(-) diff --git a/NEWS.md b/NEWS.md index b4ecccc5c4e39b..de096d226458d0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -356,6 +356,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 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 0fad04ebbc05a6..ae7ec917040992 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -283,7 +283,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/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..05ddfae468f8ec 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -330,7 +330,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)")], diff --git a/test/lib/jit_support.rb b/test/lib/jit_support.rb index 1ea8da094e2661..26f8542dc28c75 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_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_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/vm.c b/vm.c index d540aa1c8b0508..8aeb3625c9e939 100644 --- a/vm.c +++ b/vm.c @@ -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); From 69e47b7fa6611a852e86ea6efee4ba9cf49f893e Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 14 Nov 2022 23:37:30 -0800 Subject: [PATCH 02/77] MJIT: Remove reference to ROBJECT_EMBED_LEN_MAX maybe not used since some shape changes? --- lib/mjit/compiler.rb | 2 -- mjit_c.rb | 4 ---- 2 files changed, 6 deletions(-) diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 3ccdc1c925d62b..734e33e59f0461 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 ] diff --git a/mjit_c.rb b/mjit_c.rb index 05ddfae468f8ec..f2086cf94639c8 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 From 2652bc3578b79dbe9b3b28d4ee67b3729789cc61 Mon Sep 17 00:00:00 2001 From: Johnny Willemsen Date: Tue, 15 Nov 2022 09:06:22 +0100 Subject: [PATCH 03/77] [ruby/racc] Update racc.gemspec Updated homepage to https://github.com/ruby/racc --- lib/racc/racc.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 = [ From f500ca9b8a62599433f7087fe1da30ec16e3564c Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 00:18:12 -0800 Subject: [PATCH 04/77] Handle more MJIT compilation failures NotImplementedError is not part StandardError, for example. When that kind of exception happens, current_cc_unit remains not NULL, and mjit_finish ends up waiting for 5 seconds, which is inconvenient. --- lib/mjit/compiler.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 734e33e59f0461..806e7f55c49c03 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -43,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 From 1a9e87fe3a0bb8a4a16e34997371b1823557202d Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 00:11:44 -0800 Subject: [PATCH 05/77] MJIT: Fix vm_cc_cme(cc).def.type to use bit field access properly. Because the libclang node had two children, it wasn't handled well by the pattern matching for the bit field case. In addition to that, this bit field has a non-1 width. Because we're returning true/false for a width-1 bit field, I added another behavior that works like a char value for bit fields with width 2-8. --- lib/mjit/c_pointer.rb | 15 ++++++++++----- mjit_c.rb | 2 +- tool/mjit/bindgen.rb | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/mjit/c_pointer.rb b/lib/mjit/c_pointer.rb index 7f64a8ac8f4906..7dd836f1c91ef4 100644 --- a/lib/mjit/c_pointer.rb +++ b/lib/mjit/c_pointer.rb @@ -282,12 +282,17 @@ def initialize(addr, width, offset) # Dereference def * - if @width != 1 - raise NotImplementedError.new("Non-1 width is not implemented yet") + if @width == 1 + byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first + bit = (1 & (byte >> @offset)) + bit == 1 + elsif @width <= 8 && @offset == 0 + byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first + bitmask = @width.times.map { |i| 1 << i }.sum + byte & bitmask + else + raise NotImplementedError.new("not-implemented bit field access: width=#{@width} offset=#{@offset}") end - byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first - bit = 1 & (byte >> @offset) - bit == 1 end # @param width [Integer] diff --git a/mjit_c.rb b/mjit_c.rb index f2086cf94639c8..da2bab98865b0c 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -565,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/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 5ffe7166bcd685..6167bdfc000a0f 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -224,7 +224,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/) From 1eae15142f0fd9e5285e3d685bb1448f0f441b1c Mon Sep 17 00:00:00 2001 From: st0012 Date: Sun, 13 Nov 2022 11:19:40 +0000 Subject: [PATCH 06/77] [ruby/irb] Remove duplicated TestInputMethod definitions https://github.com/ruby/irb/commit/4b831d02e1 --- test/irb/helper.rb | 26 ++++++++++++++++++++++++++ test/irb/test_cmd.rb | 26 -------------------------- test/irb/test_context.rb | 26 -------------------------- test/irb/test_history.rb | 34 +++------------------------------- 4 files changed, 29 insertions(+), 83 deletions(-) diff --git a/test/irb/helper.rb b/test/irb/helper.rb index 15d5dafc57ef9d..a1dced09788471 100644 --- a/test/irb/helper.rb +++ b/test/irb/helper.rb @@ -2,6 +2,32 @@ 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..c2e8cc1774d993 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_#{$$}") diff --git a/test/irb/test_context.rb b/test/irb/test_context.rb index e7cc0bab4f4fd8..e127ba7599ab60 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 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? From 4f348e482c2b539aaf535ac3dabc07fed6718e59 Mon Sep 17 00:00:00 2001 From: st0012 Date: Wed, 5 Oct 2022 12:38:51 +0100 Subject: [PATCH 07/77] [ruby/irb] Rename leftover Reidline references https://github.com/ruby/irb/commit/0ed8b103ed --- lib/irb/ext/save-history.rb | 2 +- lib/irb/input-method.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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 From 34320d883f7197444fd240c68a65dd6cc6f395e3 Mon Sep 17 00:00:00 2001 From: st0012 Date: Wed, 5 Oct 2022 12:42:01 +0100 Subject: [PATCH 08/77] [ruby/irb] Deprecate reidline flags https://github.com/ruby/irb/commit/9957e83f7d --- lib/irb/init.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 From aecb7f6efc8422d98453128c80754121022b0766 Mon Sep 17 00:00:00 2001 From: st0012 Date: Wed, 5 Oct 2022 12:44:13 +0100 Subject: [PATCH 09/77] [ruby/irb] Deprecate USE_RELINE and USE_REIDLINE Based on this commit: https://github.com/ruby/irb/commit/93f87ec65344b44123b0150a5fc07de1fd4c80c4 It appears that the maintainer @aycabta wanted to deprecate any `USE_*LINE` configs in favor of `USE_MULTILINE`. So we should deprecate `USE_RELINE` as well. https://github.com/ruby/irb/commit/478f19f3ae --- lib/irb/context.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 From 36dc99af5aa3932f3a85b1d8212e1d3625307a90 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 15 Nov 2022 20:19:43 +0900 Subject: [PATCH 10/77] Added empty class for test methods in TestIRB cases --- test/irb/helper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/irb/helper.rb b/test/irb/helper.rb index a1dced09788471..293315879a1e8c 100644 --- a/test/irb/helper.rb +++ b/test/irb/helper.rb @@ -1,5 +1,9 @@ require "test/unit" +module IRB + class InputMethod; end +end + module TestIRB class TestCase < Test::Unit::TestCase class TestInputMethod < ::IRB::InputMethod From 5dcbe5883364c763ca5324e40716a334360c19a7 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 14 Nov 2022 10:24:55 -0500 Subject: [PATCH 11/77] Fix buffer overrun in ivars when rebuilding shapes In rb_shape_rebuild_shape, we need to increase the capacity when capacity == next_iv_index since the next ivar will be writing at index next_iv_index. This bug can be reproduced when assertions are turned on and you run the following code: class Foo def initialize @a1 = 1 @a2 = 1 @a3 = 1 @a4 = 1 @a5 = 1 @a6 = 1 @a7 = 1 end def add_ivars @a8 = 1 @a9 = 1 end end class Bar < Foo end foo = Foo.new foo.add_ivars bar = Bar.new GC.start bar.add_ivars bar.clone You will get the following crash: Assertion Failed: object.c:301:rb_obj_copy_ivar:src_num_ivs <= shape_to_set_on_dest->capacity --- shape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shape.c b/shape.c index 66f5f69a0c6de3..736f37c98bc7b1 100644 --- a/shape.c +++ b/shape.c @@ -342,7 +342,7 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape) switch (dest_shape->type) { case SHAPE_IVAR: - if (midway_shape->capacity < midway_shape->next_iv_index) { + 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); } From ac063fd4c44fc90146908e9039706a79484c6b46 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 16 Nov 2022 00:10:31 +0900 Subject: [PATCH 12/77] Depending on revision.h with VPATH --- common.mk | 4 ++-- tool/update-deps | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common.mk b/common.mk index 979e0b90de6ee1..81e25a1809b6a8 100644 --- a/common.mk +++ b/common.mk @@ -8354,7 +8354,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 +8509,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 @@ -16788,7 +16788,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 +16952,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 diff --git a/tool/update-deps b/tool/update-deps index ba94840f658117..0a69a470cd4898 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 From 9751b54971c5ad3bab29ce8af6ec50695aa43251 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Tue, 15 Nov 2022 17:26:47 +0000 Subject: [PATCH 13/77] [ruby/irb] Improve testing infra (https://github.com/ruby/irb/pull/442) * Add test_in_isolation task to run tests in isolation This simulates how tests are run in Ruby CI and can detect some issues before they're merged and break Ruby CI later. * Run test_in_isolation task on CI * Fix TestRaiseNoBacktraceException's setup https://github.com/ruby/irb/commit/51f23c58b0 --- test/irb/test_raise_no_backtrace_exception.rb | 1 + 1 file changed, 1 insertion(+) 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" From 1125274c4e8aeffd8609ced41c05d26d45b9b5ad Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 12:57:43 -0800 Subject: [PATCH 14/77] YJIT: Invalidate redefined methods only through cme (#6734) Co-authored-by: Alan Wu Co-authored-by: Alan Wu --- vm_method.c | 7 +++-- yjit.h | 2 -- yjit/src/codegen.rs | 10 +++---- yjit/src/core.rs | 6 ---- yjit/src/invariants.rs | 67 ++---------------------------------------- 5 files changed, 12 insertions(+), 80 deletions(-) diff --git a/vm_method.c b/vm_method.c index 08e91ea0392f91..bec3fc804d8076 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 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/src/codegen.rs b/yjit/src/codegen.rs index ac70cf98bda24b..72488f399c204d 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -3895,7 +3895,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); @@ -5291,7 +5291,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 +5470,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 +5891,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..523b0abe64a727 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -373,7 +373,6 @@ impl Branch { // 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, } @@ -636,7 +635,6 @@ pub extern "C" fn rb_yjit_iseq_mark(payload: *mut c_void) { // 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()) }; } @@ -692,7 +690,6 @@ 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(); } @@ -889,7 +886,6 @@ fn add_block_version(blockref: &BlockRef, cb: &CodeBlock) { // 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()); } @@ -1009,11 +1005,9 @@ impl Block { /// 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, }); } diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs index cd3214feae546d..e2db8f36b528b0 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 @@ -413,14 +358,6 @@ pub fn block_assumptions_free(blockref: &BlockRef) { if let Some(blockset) = invariants.cme_validity.get_mut(&dep.callee_cme) { 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); - } - } } } From d1fb6595475707986356fd2533fa3f2a650ea39b Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 14:50:12 -0800 Subject: [PATCH 15/77] YJIT: Count getivar side exits by receiver flag changes (#6735) --- yjit/src/codegen.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 72488f399c204d..6a3f0a3c36de74 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -2051,6 +2051,7 @@ fn gen_get_ivar( 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)); + let megamorphic_side_exit = counted_exit!(ocb, side_exit, getivar_megamorphic).into(); jit_chain_guard( JCC_JNE, jit, @@ -2058,7 +2059,7 @@ fn gen_get_ivar( asm, ocb, max_chain_depth, - side_exit, + megamorphic_side_exit, ); match ivar_index { From 0d384ce6e627539d17f9307e522cb98bbca1ace3 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 15:20:02 -0800 Subject: [PATCH 16/77] YJIT: Include actual memory region size in stats (#6736) --- yjit.rb | 1 + yjit/src/asm/mod.rs | 7 +++++-- yjit/src/stats.rs | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/yjit.rb b/yjit.rb index cac2430127881c..2d430557fdca7f 100644 --- a/yjit.rb +++ b/yjit.rb @@ -262,6 +262,7 @@ def _print_stats $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]) 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/stats.rs b/yjit/src/stats.rs index cfce4c9c33f2b6..f3d01a120e20be 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -402,6 +402,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()); From 41b0f641ef0671d8cde397e56b1eb3c6b8e0f0db Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 15:23:20 -0800 Subject: [PATCH 17/77] YJIT: Always encode Opnd::Value in 64 bits on x86_64 for GC offsets (#6733) * YJIT: Always encode Opnd::Value in 64 bits on x86_64 for GC offsets Co-authored-by: Alan Wu * Introduce heap_object_p * Leave original mov intact * Remove unneeded branches * Add a test for movabs Co-authored-by: Alan Wu --- yjit/src/asm/x86_64/mod.rs | 14 ++++++++++++++ yjit/src/asm/x86_64/tests.rs | 6 ++++++ yjit/src/backend/x86_64/mod.rs | 10 +++++----- yjit/src/cruby.rs | 5 +++++ 4 files changed, 30 insertions(+), 5 deletions(-) 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/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/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; From e7443dbbcaf82eee200d33a5c3bc374ad9eec8a7 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 21:34:30 -0800 Subject: [PATCH 18/77] Rewrite Symbol#to_sym and #intern in Ruby (#6683) --- .document | 1 + common.mk | 5 +++++ inits.c | 1 + string.c | 19 ------------------- symbol.c | 2 ++ symbol.rb | 15 +++++++++++++++ 6 files changed, 24 insertions(+), 19 deletions(-) create mode 100644 symbol.rb 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/common.mk b/common.mk index 81e25a1809b6a8..404388e935891b 100644 --- a/common.mk +++ b/common.mk @@ -1104,6 +1104,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 \ @@ -9600,6 +9601,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 @@ -15425,6 +15427,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 +15600,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 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/string.c b/string.c index 73be76cd7235fb..50da6dc6211197 100644 --- a/string.c +++ b/string.c @@ -11524,23 +11524,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) { @@ -12107,8 +12090,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/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 From cd2da0941035c231bc95007a269b6dc3d4b62ca9 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 21:59:38 -0800 Subject: [PATCH 19/77] MJIT: Refactor BitField dereference a little --- lib/mjit/c_pointer.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/mjit/c_pointer.rb b/lib/mjit/c_pointer.rb index 7dd836f1c91ef4..aadf80e804e86d 100644 --- a/lib/mjit/c_pointer.rb +++ b/lib/mjit/c_pointer.rb @@ -282,12 +282,11 @@ def initialize(addr, width, offset) # Dereference def * + byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first if @width == 1 - byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first bit = (1 & (byte >> @offset)) bit == 1 elsif @width <= 8 && @offset == 0 - byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first bitmask = @width.times.map { |i| 1 << i }.sum byte & bitmask else From fca50abb7c7e5369fdb8d2d3b6c1750076f3d846 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 15 Nov 2022 23:42:41 -0800 Subject: [PATCH 20/77] Let mjit-bindgen use BASERUBY and bundle/inline (#6740) --- .github/workflows/mjit-bindgen.yml | 36 +++++++++++++----------------- common.mk | 14 +----------- tool/mjit/.gitignore | 1 - tool/mjit/Gemfile | 3 --- tool/mjit/bindgen.rb | 19 ++++++++++------ 5 files changed, 28 insertions(+), 45 deletions(-) delete mode 100644 tool/mjit/.gitignore delete mode 100644 tool/mjit/Gemfile 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/common.mk b/common.mk index 404388e935891b..1d11753fa5b05f 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. 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 6167bdfc000a0f..688d48ccb2f537 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, From be65cf53253fa955cd4016459b3add3ba6046837 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 16 Nov 2022 11:41:19 +0900 Subject: [PATCH 21/77] Remove `-j` option from `MFLAGS` for sub-makes --- ext/extmk.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/extmk.rb b/ext/extmk.rb index 73ee71fd376241..9c12bda919c217 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -721,6 +721,7 @@ 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 def mf.macro(name, values, max = 70) From dc1c4e46758ace2c9e5e822df0d64b16bb564bb4 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 16 Nov 2022 11:43:52 +0900 Subject: [PATCH 22/77] Clean extension build directories and exts.mk file --- ext/extmk.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/extmk.rb b/ext/extmk.rb index 9c12bda919c217..cc560be1f22c93 100755 --- a/ext/extmk.rb +++ b/ext/extmk.rb @@ -722,6 +722,7 @@ def initialize(src) 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) @@ -764,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| @@ -798,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)" From 1f4f6c9832d83e7ebd65ccf4e95cef358b3512c6 Mon Sep 17 00:00:00 2001 From: S-H-GAMELINKS Date: Tue, 15 Nov 2022 13:24:08 +0900 Subject: [PATCH 23/77] Using UNDEF_P macro --- array.c | 18 +++++++-------- class.c | 6 ++--- compar.c | 2 +- compile.c | 4 ++-- complex.c | 8 +++---- cont.c | 6 ++--- dir.c | 4 ++-- enum.c | 60 ++++++++++++++++++++++++------------------------ enumerator.c | 34 +++++++++++++-------------- error.c | 32 +++++++++++++------------- eval.c | 16 ++++++------- eval_error.c | 4 ++-- gc.c | 12 +++++----- hash.c | 30 ++++++++++++------------ io.c | 38 +++++++++++++++--------------- io_buffer.c | 8 +++---- iseq.c | 4 ++-- marshal.c | 6 ++--- numeric.c | 16 ++++++------- object.c | 10 ++++---- parse.y | 12 +++++----- proc.c | 12 +++++----- process.c | 2 +- ractor.c | 28 +++++++++++----------- range.c | 14 +++++------ rational.c | 10 ++++---- re.c | 4 ++-- scheduler.c | 4 ++-- signal.c | 2 +- sprintf.c | 8 +++---- string.c | 8 +++---- struct.c | 2 +- thread.c | 16 ++++++------- time.c | 16 ++++++------- transcode.c | 2 +- transient_heap.c | 2 +- variable.c | 40 ++++++++++++++++---------------- vm.c | 12 +++++----- vm_args.c | 6 ++--- vm_eval.c | 12 +++++----- vm_exec.c | 2 +- vm_insnhelper.c | 30 ++++++++++++------------ vm_method.c | 4 ++-- vm_trace.c | 14 +++++------ 44 files changed, 290 insertions(+), 290 deletions(-) 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/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/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/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/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/gc.c b/gc.c index 8b8f0c11f198da..5a5ec869ca0e56 100644 --- a/gc.c +++ b/gc.c @@ -4307,7 +4307,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); @@ -4673,7 +4673,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)) { @@ -7379,7 +7379,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)); @@ -12790,7 +12790,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."); } @@ -13081,14 +13081,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/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/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/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 f0117d1a47d992..7f4404c46217b1 100644 --- a/object.c +++ b/object.c @@ -125,7 +125,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)); @@ -138,7 +138,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); @@ -411,7 +411,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; @@ -2545,7 +2545,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)) @@ -2975,7 +2975,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..a69a7bea55f2b9 100644 --- a/parse.y +++ b/parse.y @@ -10848,7 +10848,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 +11501,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 +11541,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 { @@ -13787,7 +13787,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 +13898,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 +14159,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..dabe9320f297fe 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; 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/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/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/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 50da6dc6211197..0ae36e5c727471 100644 --- a/string.c +++ b/string.c @@ -495,7 +495,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(); @@ -1873,10 +1873,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; @@ -8922,7 +8922,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)) { 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/thread.c b/thread.c index 624c070877bfc7..1b12ec207703ce 100644 --- a/thread.c +++ b/thread.c @@ -681,7 +681,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); @@ -727,7 +727,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); @@ -1027,7 +1027,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 @@ -1219,7 +1219,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; } @@ -2349,7 +2349,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 */ || @@ -5035,7 +5035,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)) { @@ -5067,7 +5067,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 { @@ -5094,7 +5094,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/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..daae90396e1fb3 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; @@ -1756,7 +1756,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 +1773,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 +2283,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 +2468,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 +2482,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 +2578,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 +2617,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 +2652,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 +2725,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 +2769,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 +2798,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 +2935,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 +3092,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 +3298,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 +3398,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 +3508,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 +3838,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 8aeb3625c9e939..7bb17036dcb5da 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)); @@ -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 bec3fc804d8076..f1c50b9300ebc7 100644 --- a/vm_method.c +++ b/vm_method.c @@ -2738,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; } @@ -2850,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; From 24cbc50c76f7ccc89f9510250359f3a405d9af68 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 5 Nov 2022 01:01:30 +0900 Subject: [PATCH 24/77] Remove duplicate `.rbinc` on `.rb` dependencies --- common.mk | 9 --------- tool/update-deps | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/common.mk b/common.mk index 1d11753fa5b05f..fbcb3c3551aaf2 100644 --- a/common.mk +++ b/common.mk @@ -6737,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 @@ -6956,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 @@ -9803,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 @@ -10019,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 @@ -10413,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 @@ -10611,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 @@ -11656,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 @@ -15815,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 @@ -18222,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/tool/update-deps b/tool/update-deps index 0a69a470cd4898..f142b30be82bde 100755 --- a/tool/update-deps +++ b/tool/update-deps @@ -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 } From 05af417587cf57142b66e5041b592fe67803a3db Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 16 Nov 2022 10:08:52 -0800 Subject: [PATCH 25/77] YJIT: Show YJIT build option in RUBY_DESCRIPTION (#6738) YJIT: Show YJIT profile in RUBY_DESCRIPTION --- configure.ac | 3 +++ test/ruby/test_rubyoptions.rb | 2 +- version.c | 7 ++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index ca234e3c900ffc..f6b81c4c15e6d3 100644 --- a/configure.ac +++ b/configure.ac @@ -3821,6 +3821,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/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/version.c b/version.c index a2d278f2db1ec5..653a4238a9b59d 100644 --- a/version.c +++ b/version.c @@ -60,6 +60,11 @@ const int ruby_api_version[] = { #ifndef RUBY_FULL_REVISION # define RUBY_FULL_REVISION RUBY_REVISION #endif +#ifdef YJIT_SUPPORT +#define YJIT_DESCRIPTION " +YJIT " STRINGIZE(YJIT_SUPPORT) +#else +#define YJIT_DESCRIPTION " +YJIT" +#endif const char ruby_version[] = RUBY_VERSION; const char ruby_revision[] = RUBY_FULL_REVISION; const char ruby_release_date[] = RUBY_RELEASE_DATE; @@ -67,7 +72,7 @@ const char ruby_platform[] = RUBY_PLATFORM; const int ruby_patchlevel = RUBY_PATCHLEVEL; const char ruby_description[] = RUBY_DESCRIPTION_WITH(""); static const char ruby_description_with_mjit[] = RUBY_DESCRIPTION_WITH(" +MJIT"); -static const char ruby_description_with_yjit[] = RUBY_DESCRIPTION_WITH(" +YJIT"); +static const char ruby_description_with_yjit[] = RUBY_DESCRIPTION_WITH(YJIT_DESCRIPTION); const char ruby_copyright[] = "ruby - Copyright (C) " RUBY_BIRTH_YEAR_STR "-" RUBY_RELEASE_YEAR_STR " " RUBY_AUTHOR; From 3eb7a6521ce08eb3758e63575a90b66a3ea10717 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 16 Nov 2022 10:09:15 -0800 Subject: [PATCH 26/77] YJIT: Shrink the vectors of Block after mutation (#6739) --- yjit/src/codegen.rs | 4 +--- yjit/src/core.rs | 42 ++++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 6a3f0a3c36de74..9551ece3c94790 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -829,9 +829,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()); diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 523b0abe64a727..adce0f51ded173 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -996,20 +996,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, - callee_cme: *const rb_callable_method_entry_t, - ) { - self.cme_dependencies.push(CmeDependency { - callee_cme, - }); + pub fn add_cme_dependency(&mut self, callee_cme: *const rb_callable_method_entry_t) { + self.cme_dependencies.push(CmeDependency { 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 @@ -1502,8 +1513,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()); @@ -1662,7 +1672,7 @@ 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()); return branchref; } @@ -1793,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; @@ -1864,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()); @@ -2011,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()); From 6de4032e407b5e4bcf837332b9980a5892282df8 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 16 Nov 2022 15:30:29 -0800 Subject: [PATCH 27/77] YJIT: Stop wrapping CmePtr with CmeDependency (#6747) * YJIT: Stop wrapping CmePtr with CmeDependency * YJIT: Fix an outdated comment [ci skip] --- yjit/src/core.rs | 26 +++++++++++--------------- yjit/src/invariants.rs | 2 +- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/yjit/src/core.rs b/yjit/src/core.rs index adce0f51ded173..d07f8e0c8c1f6c 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -369,12 +369,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 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 @@ -411,7 +407,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. @@ -634,8 +630,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.callee_cme.into()) }; + for &cme_dep in &block.cme_dependencies { + unsafe { rb_gc_mark_movable(cme_dep.into()) }; } // Mark outgoing branch entries @@ -690,7 +686,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.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 @@ -885,8 +881,8 @@ fn add_block_version(blockref: &BlockRef, cb: &CodeBlock) { // 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.callee_cme.into()); + for &dep in block.iter_cme_deps() { + obj_written!(iseq, dep.into()); } // Run write barriers for all objects in generated code. @@ -968,7 +964,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() } @@ -1006,8 +1002,8 @@ impl Block { /// Instantiate a new CmeDependency struct and add it to the list of /// dependencies for this block. - pub fn add_cme_dependency(&mut self, callee_cme: *const rb_callable_method_entry_t) { - self.cme_dependencies.push(CmeDependency { callee_cme }); + pub fn add_cme_dependency(&mut self, callee_cme: CmePtr) { + self.cme_dependencies.push(callee_cme); self.cme_dependencies.shrink_to_fit(); } diff --git a/yjit/src/invariants.rs b/yjit/src/invariants.rs index e2db8f36b528b0..b911ff2c921a5f 100644 --- a/yjit/src/invariants.rs +++ b/yjit/src/invariants.rs @@ -355,7 +355,7 @@ 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); } } From 1b8236acc212a6751da7248eb3f22b0262ca0623 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 16 Nov 2022 15:31:13 -0800 Subject: [PATCH 28/77] YJIT: Add compiled_branch_count stats (#6746) --- yjit.rb | 3 ++- yjit/src/core.rs | 1 + yjit/src/stats.rs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/yjit.rb b/yjit.rb index 2d430557fdca7f..eb5376c9c5a217 100644 --- a/yjit.rb +++ b/yjit.rb @@ -254,8 +254,9 @@ 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]) diff --git a/yjit/src/core.rs b/yjit/src/core.rs index d07f8e0c8c1f6c..f2bde4a010997e 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -1669,6 +1669,7 @@ 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().push_outgoing(branchref.clone()); + incr_counter!(compiled_branch_count); return branchref; } diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index f3d01a120e20be..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, From 3259aceb3514892450664828b39a78cc95a412fa Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 16 Nov 2022 15:48:46 -0800 Subject: [PATCH 29/77] YJIT: Pack BlockId and CodePtr (#6748) --- yjit/src/codegen.rs | 3 ++- yjit/src/core.rs | 7 +++++-- yjit/src/disasm.rs | 3 ++- yjit/src/virtualmem.rs | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 9551ece3c94790..0c51f589db7f21 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 diff --git a/yjit/src/core.rs b/yjit/src/core.rs index f2bde4a010997e..ad74067ea0f0cc 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, @@ -1526,7 +1527,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)); } } @@ -2148,7 +2150,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); } } } 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/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] From a777ec0d85f1d52ddf531931c7457a65961f0082 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Wed, 16 Nov 2022 16:30:39 -0800 Subject: [PATCH 30/77] YJIT: Shrink version lists after mutation (#6749) --- yjit/src/core.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/yjit/src/core.rs b/yjit/src/core.rs index ad74067ea0f0cc..78b4cbdb0c91a9 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -878,6 +878,7 @@ 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. From e61b3e6f43434bd815577a67d4f1719091b57d5c Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Fri, 30 Sep 2022 14:26:46 -0400 Subject: [PATCH 31/77] [ruby/irb] Fix https://github.com/ruby/irb/pull/295: Ignore Java package reference objects in JRuby https://github.com/ruby/irb/commit/84d030182d --- lib/irb/completion.rb | 1 + 1 file changed, 1 insertion(+) 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! From 923c1aaed7a5707d5c358627aad37f29e7738143 Mon Sep 17 00:00:00 2001 From: st0012 Date: Mon, 25 Jul 2022 23:35:25 +0100 Subject: [PATCH 32/77] Drop Ruby 2.5 support Because it has reached EOL for more than 1.5 years and it won't be supported by the next reline version either. --- lib/irb.rb | 6 +----- lib/irb/irb.gemspec | 2 +- test/irb/test_context.rb | 6 +++--- test/irb/test_ruby_lex.rb | 3 --- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/irb.rb b/lib/irb.rb index 04009664efd47a..ab5702c9f2251d 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -598,11 +598,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 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/test/irb/test_context.rb b/test/irb/test_context.rb index e127ba7599ab60..4d256a73a20e4c 100644 --- a/test/irb/test_context.rb +++ b/test/irb/test_context.rb @@ -492,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/, @@ -523,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/, @@ -560,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_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()), From c76909e551f0f60b7a354ab748ef1a04b84d06fb Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 17 Nov 2022 13:52:12 +0900 Subject: [PATCH 33/77] [ruby/irb] Bump up 1.4.3 https://github.com/ruby/irb/commit/1bddbbf602 --- lib/irb/version.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 71e668e63383030adc06893d0b16a16e9abdabce Mon Sep 17 00:00:00 2001 From: git Date: Thu, 17 Nov 2022 04:53:46 +0000 Subject: [PATCH 34/77] Update default gems list at c76909e551f0f60b7a354ab748ef1a [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index de096d226458d0..7cd1533512d228 100644 --- a/NEWS.md +++ b/NEWS.md @@ -239,7 +239,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 From 7b1d23fd295fe8275b313f31ea545f7f6b3f2e62 Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Thu, 17 Nov 2022 10:57:52 +0100 Subject: [PATCH 35/77] Windows: Readlink improvements (#6745) * Windows: Use readlink emulation for File.readlink This fixes readlink emulation for the ERROR_MORE_DATA case and general error reporting. It now releases GVL while readlink IO operation. The dedicated rb_readlink was introduced in commit 2ffb87995a33cdc7ba609a4b867f03f18da0c3b3 in order to improve encoding and buffer allocation. However the encoding issues are solved since ruby-3.0 switched to UTF-8 and the buffer allocation will be improved in a later commit. * Windows: Increase the default buffer size for reparse point info So far nearly all queries of reparse points needed two attempts to get enough buffer. * Windows: Remove declaration of rb_w32_wreadlink It was removed in commit 2f6fdd3aebdee2ce04d003b206f6da78120e8235 --- file.c | 4 ++-- include/ruby/win32.h | 1 - win32/file.c | 44 -------------------------------------------- win32/file.h | 8 +++----- win32/win32.c | 31 +++++++++++++++++-------------- 5 files changed, 22 insertions(+), 66 deletions(-) diff --git a/file.c b/file.c index 93f5898ccde0b8..3e772bd099db9c 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 @@ -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 diff --git a/include/ruby/win32.h b/include/ruby/win32.h index 93e6183ed990a2..e03f3459588837 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -302,7 +302,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 *); 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..ef701487dd190b 100644 --- a/win32/file.h +++ b/win32/file.h @@ -1,10 +1,8 @@ #ifndef RUBY_WIN32_FILE_H #define RUBY_WIN32_FILE_H -#define MAX_REPARSE_PATH_LEN 4092 - enum { - MINIMUM_REPARSE_BUFFER_PATH_LEN = 4 + MINIMUM_REPARSE_BUFFER_PATH_LEN = 100 }; /* License: Ruby's */ typedef struct { @@ -18,14 +16,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..3ddfe9bfdf1c8b 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -5144,32 +5144,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; } From a145a204ec01a5087a3d3699e3707b41afd5fd6b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 17 Nov 2022 21:50:40 +0900 Subject: [PATCH 36/77] [DOC] Update about `sec` argument of `Time.new` --- timev.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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. From 90bfac296ec419b22be9ecb1eb7bf189ebc55243 Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Wed, 16 Nov 2022 23:40:44 +0900 Subject: [PATCH 37/77] Add OP_CCLASS_MB case --- regexec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/regexec.c b/regexec.c index 75ffb86c4510dc..16b155b8478532 100644 --- a/regexec.c +++ b/regexec.c @@ -496,6 +496,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: From 189e3c0ada43e4adb00d601dfdd26cdc42cffb2d Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Thu, 17 Nov 2022 13:58:08 +0900 Subject: [PATCH 38/77] Add default cases for cache point finding function --- regexec.c | 94 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/regexec.c b/regexec.c index 16b155b8478532..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; @@ -558,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: @@ -630,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: @@ -649,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 */ @@ -861,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) @@ -3820,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; From 0446d961a09cf324636f6dc0603ae60282c6f1c1 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 17 Nov 2022 10:58:56 -0800 Subject: [PATCH 39/77] YJIT: Fix typo in stats references (#6753) --- yjit.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yjit.rb b/yjit.rb index eb5376c9c5a217..a99560acf6d56c 100644 --- a/yjit.rb +++ b/yjit.rb @@ -270,8 +270,8 @@ def _print_stats $stderr.puts "code_gc_count: " + ("%10d" % stats[:code_gc_count]) $stderr.puts "num_gc_obj_refs: " + ("%10d" % stats[:num_gc_obj_refs]) $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?(:vm_insns_count) $stderr.puts "vm_insns_count: " + ("%10d" % stats[:vm_insns_count]) end From 3f3a53919769bc3035d7837eb2f7890d7b675f86 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 17 Nov 2022 11:31:56 -0800 Subject: [PATCH 40/77] YJIT: Add missing key for non-stats build --- yjit.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yjit.rb b/yjit.rb index a99560acf6d56c..0bd0fb8c7e80b9 100644 --- a/yjit.rb +++ b/yjit.rb @@ -163,6 +163,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 @@ -271,7 +272,7 @@ def _print_stats $stderr.puts "num_gc_obj_refs: " + ("%10d" % stats[:num_gc_obj_refs]) $stderr.puts "side_exit_count: " + ("%10d" % stats[:side_exit_count]) $stderr.puts "total_exit_count: " + ("%10d" % stats[:total_exit_count]) - $stderr.puts "total_insns_count: " + ("%10d" % stats[:total_insns_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 From 4b29eb17f2b01442198c81c5f62fcd3045aab659 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 17 Nov 2022 14:43:46 -0500 Subject: [PATCH 41/77] Fix indentation of switch statement in shape.c --- shape.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/shape.c b/shape.c index 736f37c98bc7b1..bb51abecbf609e 100644 --- a/shape.c +++ b/shape.c @@ -341,21 +341,21 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_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); - } + 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: + break; } return midway_shape; From c80edc9f98f58826d6c6d4b5ee11a2886bbfb006 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 17 Nov 2022 12:59:59 -0800 Subject: [PATCH 42/77] YJIT: Add object shape count to stats (#6754) --- yjit.c | 8 ++++++++ yjit.rb | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) 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.rb b/yjit.rb index 0bd0fb8c7e80b9..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] @@ -270,6 +273,7 @@ def _print_stats $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[:total_exit_count]) $stderr.puts "total_insns_count: " + ("%10d" % stats[:total_insns_count]) if stats.key?(:total_insns_count) From 4e4b29b1a9e534554594b6f18fc0bdc462638934 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 17 Nov 2022 14:00:25 -0800 Subject: [PATCH 43/77] YJIT: Make the code GC test stabler The first `add_pages` call shouldn't hit Code GC yet; otherwise `compiles` returns false. With the increased code size in runtime_stats itself, it sometimes hits Code GC too early, at least in arm64 that has a large code size. --- test/ruby/test_yjit.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From ea8a7287e2b96b9c24e5e89fe863e5bfa60bfdda Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 17 Nov 2022 14:50:25 -0800 Subject: [PATCH 44/77] Add support for `sockaddr_un` on Windows. (#6513) * Windows: Fix warning about undefined if_indextoname() * Windows: Fix UNIXSocket on MINGW and make .pair more reliable * Windows: Use nonblock=true for read tests with scheduler * Windows: Move socket detection from File.socket? to File.stat Add S_IFSOCK to Windows and interpret reparse points accordingly. Enable tests that work now. * Windows: Use wide-char functions to UNIXSocket This fixes behaviour with non-ASCII characters. It also fixes deletion of temporary UNIXSocket.pair files. * Windows: Add UNIXSocket tests for specifics of Windows impl. * Windows: fix VC build due to missing _snwprintf Avoid usage of _snwprintf, since it fails linking ruby.dll like so: linking shared-library x64-vcruntime140-ruby320.dll x64-vcruntime140-ruby320.def : error LNK2001: unresolved external symbol snwprintf x64-vcruntime140-ruby320.def : error LNK2001: unresolved external symbol vsnwprintf_l whereas linking miniruby.exe succeeds. This patch uses snprintf on the UTF-8 string instead. Also remove branch GetWindowsDirectoryW, since it doesn't work. * Windows: Fix dangling symlink test failures Co-authored-by: Lars Kanis --- NEWS.md | 9 +- configure.ac | 5 + ext/socket/extconf.rb | 1 + ext/socket/init.c | 2 +- ext/socket/lib/socket.rb | 2 +- ext/socket/option.c | 10 +- ext/socket/raddrinfo.c | 20 ++-- ext/socket/rubysocket.h | 12 ++- ext/socket/socket.c | 6 +- ext/socket/unixserver.c | 4 +- ext/socket/unixsocket.c | 8 +- file.c | 3 +- include/ruby/win32.h | 6 +- test/fiber/test_enumerator.rb | 6 -- test/fiber/test_io.rb | 23 ++--- test/fileutils/test_fileutils.rb | 4 + test/ruby/test_file_exhaustive.rb | 2 +- test/ruby/test_io.rb | 24 ++++- test/ruby/test_io_timeout.rb | 6 -- test/socket/test_nonblock.rb | 4 +- test/socket/test_unix.rb | 147 ++++++++++++++++++++-------- win32/Makefile.sub | 3 + win32/file.h | 4 + win32/win32.c | 154 ++++++++++++++++++++++++++---- 24 files changed, 340 insertions(+), 125 deletions(-) diff --git a/NEWS.md b/NEWS.md index 7cd1533512d228..a4b4ae6bd876cc 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 @@ -417,3 +423,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/configure.ac b/configure.ac index f6b81c4c15e6d3..d11aae2170a9d4 100644 --- a/configure.ac +++ b/configure.ac @@ -1283,6 +1283,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) 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 3e772bd099db9c..10eb1081e328a9 100644 --- a/file.c +++ b/file.c @@ -1765,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; } @@ -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/include/ruby/win32.h b/include/ruby/win32.h index e03f3459588837..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. */ @@ -392,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/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/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/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..b1dcc813e7da81 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, s2 = 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/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.h b/win32/file.h index ef701487dd190b..4f1f36a75cd72a 100644 --- a/win32/file.h +++ b/win32/file.h @@ -1,6 +1,10 @@ #ifndef RUBY_WIN32_FILE_H #define RUBY_WIN32_FILE_H +#ifndef IO_REPARSE_TAG_AF_UNIX +# define IO_REPARSE_TAG_AF_UNIX 0x80000023 +#endif + enum { MINIMUM_REPARSE_BUFFER_PATH_LEN = 100 }; diff --git a/win32/win32.c b/win32/win32.c index 3ddfe9bfdf1c8b..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; @@ -5632,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; @@ -5710,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); @@ -5753,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); @@ -5767,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; } @@ -5788,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; } From cb4c89e08ea2f879b6a860a3d6944efce6c9bf50 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 17 Nov 2022 15:19:21 -0800 Subject: [PATCH 45/77] Fix quoting of code in `NEWS.md`. --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index a4b4ae6bd876cc..b5a59091698526 100644 --- a/NEWS.md +++ b/NEWS.md @@ -117,7 +117,7 @@ Note: We're only listing outstanding class updates. ``` * UNIXSocket - * Add support for UNIXSocket on Windows. Emulate anonymous sockets. Add + * Add support for `UNIXSocket` on Windows. Emulate anonymous sockets. Add support for `File.socket?` and `File::Stat#socket?` where possible. [[Feature #19135]] From d8202a52a5fedd2209248b030ea6c41382f5f3e2 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 17 Nov 2022 16:47:49 -0800 Subject: [PATCH 46/77] [ruby/irb] Add an option to suppress code_around_binding (https://github.com/ruby/irb/pull/444) for debug.gem's `irb` command --- lib/irb.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/irb.rb b/lib/irb.rb index ab5702c9f2251d..1c38960cc06e69 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -924,10 +924,10 @@ 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) From ab4379e086d69b13f4446bb0bddd87844650ea1a Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 17 Nov 2022 17:55:24 -0800 Subject: [PATCH 47/77] Refactor RB_SPECIAL_CONST_P (#6759) Since https://github.com/ruby/ruby/pull/6599, RUBY_IMMEDIATE_MASK also overlaps RUBY_Qnil. Now RB_SPECIAL_CONST_P seems confusing since both RB_IMMEDIATE_P and RB_TEST check for RUBY_Qnil while we only need to check RUBY_Qnil besides RUBY_IMMEDIATE_MASK. I'd like to make this change to make it less confusing. I confirmed that this doesn't change the number of instructions used for the RUBY_Qfalse check on Linux x86_64 GCC and macOS arm64 Clang. --- include/ruby/internal/special_consts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() From 98e9165b0ac0065b0594c41f7d9f8907e5b1e623 Mon Sep 17 00:00:00 2001 From: Jimmy Miller Date: Thu, 17 Nov 2022 23:17:40 -0500 Subject: [PATCH 48/77] Fix bug involving .send and overwritten methods. (#6752) @casperisfine reporting a bug in this gist https://gist.github.com/casperisfine/d59e297fba38eb3905a3d7152b9e9350 After investigating I found it was caused by a combination of send and a c_func that we have overwritten in the JIT. For send calls, we need to do some stack manipulation before making the call. Because of the way exits works, we need to do that stack manipulation at the last possible moment. In this case, we weren't doing that stack manipulation at all. Unfortunately, with how the code is structured there isn't a great place to do that stack manipulation for our overridden C funcs. Each overridden C func can return a boolean stating that it shouldn't be used. We would need to do the stack manipulation after all of those checks are done. We could pass a lambda(?) or separate out the logic for "can I run this override" from "now generate the code for it". Since we are coming up on a release, I went with the path of least resistence and just decided to not use these overrides if we are in a send call. We definitely should revist this in the future. --- bootstraptest/test_yjit.rb | 23 +++++++++++++++++++++++ yjit/src/codegen.rs | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index c5bb681e841ab7..175f5bd86cc0f7 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -45,6 +45,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/yjit/src/codegen.rs b/yjit/src/codegen.rs index 0c51f589db7f21..be4fc423b6f47c 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4179,7 +4179,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) { From bd148a2bdd0c1a4d7679eedcd649171cdb4234d7 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 18 Nov 2022 14:38:09 +0900 Subject: [PATCH 49/77] tool/generic_erb.rb: Use ERB#result_with_hash instead of #result to prevent the warnings: ``` ./tool/generic_erb.rb:23: warning: assigned but unused variable - output ./tool/generic_erb.rb:24: warning: assigned but unused variable - vpath ``` --- tool/generic_erb.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/generic_erb.rb b/tool/generic_erb.rb index 47bffd830c81dd..1bb740900a9bf2 100644 --- a/tool/generic_erb.rb +++ b/tool/generic_erb.rb @@ -30,7 +30,7 @@ erb = ERB.new(File.read(template), nil, '%-') end erb.filename = template - source ? erb.src : proc{erb.result(binding)}.call + source ? erb.src : proc{erb.result_with_hash({ erb: erb, output: output, vpath: vpath })}.call end result = result.size == 1 ? result[0] : result.join("") out.write(result) From 096ffebbbf1f4a5bbdb3abb4b8335b43645cc870 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 18 Nov 2022 14:39:40 +0900 Subject: [PATCH 50/77] Prevent a "warning: assigned but unused variable - s2" --- test/socket/test_unix.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/socket/test_unix.rb b/test/socket/test_unix.rb index b1dcc813e7da81..e206339db09596 100644 --- a/test/socket/test_unix.rb +++ b/test/socket/test_unix.rb @@ -540,7 +540,7 @@ def test_windows_unix_socket_pair_with_umlaut ENV['TMP'] = File.join(Dir.tmpdir, "äöü€") FileUtils.mkdir_p ENV['TMP'] - s1, s2 = UNIXSocket.pair + s1, = UNIXSocket.pair assert !s1.path.empty? assert !File.exist?(s1.path) ensure From e19afe36ca64b261f25622219d570720fd20a28e Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 18 Nov 2022 14:45:21 +0900 Subject: [PATCH 51/77] Revert "tool/generic_erb.rb: Use ERB#result_with_hash instead of #result" This reverts commit bd148a2bdd0c1a4d7679eedcd649171cdb4234d7. ERB#result_with_hash does not work on Ruby 2.2 https://ci.appveyor.com/project/ruby/ruby/builds/45420170 ``` ../tool/generic_erb.rb:33:in `block (2 levels) in
': undefined method `result_with_hash' for # (NoMethodError) ``` --- tool/generic_erb.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/generic_erb.rb b/tool/generic_erb.rb index 1bb740900a9bf2..47bffd830c81dd 100644 --- a/tool/generic_erb.rb +++ b/tool/generic_erb.rb @@ -30,7 +30,7 @@ erb = ERB.new(File.read(template), nil, '%-') end erb.filename = template - source ? erb.src : proc{erb.result_with_hash({ erb: erb, output: output, vpath: vpath })}.call + source ? erb.src : proc{erb.result(binding)}.call end result = result.size == 1 ? result[0] : result.join("") out.write(result) From 417f3cd89365a12c1cd49f0df842a3dc2eb040b3 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 18 Nov 2022 14:56:55 +0900 Subject: [PATCH 52/77] tool/generic_erb.rb: Add a hack to prevent "unused variable" warnings --- tool/generic_erb.rb | 3 +++ 1 file changed, 3 insertions(+) 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: '%-') From ca1aa7afeae43b02d00cf2f2a82acde293e48875 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Fri, 18 Nov 2022 15:39:50 +0900 Subject: [PATCH 53/77] Prevent a "method redefined" warning http://rubyci.s3.amazonaws.com/ubuntu2004-arm/ruby-master/log/20221118T033003Z.log.html.gz ``` [21350/22024] TestIRB::ExtendCommandTest#test_show_source_end_finder/home/chkbuild/chkbuild/tmp/build/20221118T033003Z/ruby/test/irb/test_cmd.rb:523: warning: method redefined; discarding old show_source_test_method /home/chkbuild/chkbuild/tmp/build/20221118T033003Z/ruby/test/irb/test_cmd.rb:523: warning: previous definition of show_source_test_method was here ``` --- test/irb/test_cmd.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb index c2e8cc1774d993..bcfb1d0b86511b 100644 --- a/test/irb/test_cmd.rb +++ b/test/irb/test_cmd.rb @@ -523,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( From a80b66e7ebf1179964b416cee6fd236048a73dcf Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 18 Nov 2022 17:51:33 +0900 Subject: [PATCH 54/77] Added build instructions of psych and fiddle with source files of libyaml and libffi --- NEWS.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index b5a59091698526..bff9d1e7ab51b4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -307,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 From 00872d120b017de2270fe18068cea2d37d41ed71 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 18 Nov 2022 09:11:19 +0000 Subject: [PATCH 55/77] [ruby/irb] Add debug command (https://github.com/ruby/irb/pull/446) https://github.com/ruby/irb/commit/30faa13fa3 --- lib/irb.rb | 13 +++++++++++++ lib/irb/cmd/debug.rb | 33 +++++++++++++++++++++++++++++++++ lib/irb/extend-command.rb | 4 ++++ 3 files changed, 50 insertions(+) create mode 100644 lib/irb/cmd/debug.rb diff --git a/lib/irb.rb b/lib/irb.rb index 1c38960cc06e69..1cd89d295cc255 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -393,6 +393,8 @@ def IRB.irb_abort(irb, exception = Abort) end class Irb + DIR_NAME = __dir__ + ASSIGNMENT_NODE_TYPES = [ # Local, instance, global, class, constant, instance, and index assignment: # "foo = bar", @@ -434,6 +436,16 @@ def initialize(workspace = nil, input_method = nil) @scanner = RubyLex.new end + def debug_break + # it means the debug command is executed + if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:original_capture_frames) + # after leaving this initial breakpoint, revert the capture_frames patch + DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :original_capture_frames) + # and remove the redundant method + DEBUGGER__.singleton_class.send(:undef_method, :original_capture_frames) + end + end + def run(conf = IRB.conf) conf[:IRB_RC].call(context) if conf[:IRB_RC] conf[:MAIN_CONTEXT] = context @@ -931,5 +943,6 @@ def irb(show_code: true) 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..8aab40cf84afb5 --- /dev/null +++ b/lib/irb/cmd/debug.rb @@ -0,0 +1,33 @@ +require_relative "nop" + +module IRB + # :stopdoc: + + module ExtendCommand + class Debug < Nop + def execute(*args) + require "debug/session" + DEBUGGER__.start(nonstop: true) + DEBUGGER__.singleton_class.send(:alias_method, :original_capture_frames, :capture_frames) + + def DEBUGGER__.capture_frames(skip_path_prefix) + frames = original_capture_frames(skip_path_prefix) + frames.reject! do |frame| + frame.realpath&.start_with?(::IRB::Irb::DIR_NAME) || frame.path.match?(/internal:prelude/) + end + frames + end + + 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 + rescue LoadError => e + 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 + end + end + end +end diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index acc23c9920f1ca..7da75fe1477d90 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], From ddd62fadaf91418cd259593285bc59358fb0b166 Mon Sep 17 00:00:00 2001 From: Shugo Maeda Date: Wed, 16 Nov 2022 23:03:39 +0900 Subject: [PATCH 56/77] Allow anonymous keyword rest parameter with other keyword parameters Fixes [Bug #19132] --- parse.y | 2 +- test/ruby/test_ast.rb | 2 +- test/ruby/test_syntax.rb | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/parse.y b/parse.y index a69a7bea55f2b9..f5f8e15acf3ba6 100644 --- a/parse.y +++ b/parse.y @@ -5612,7 +5612,7 @@ f_kwrest : kwrest_mark tIDENTIFIER { arg_var(p, ANON_KEYWORD_REST_ID); /*%%%*/ - $$ = internal_id(p); + $$ = ANON_KEYWORD_REST_ID; /*% %*/ /*% ripper: kwrest_param!(Qnil) %*/ } diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 0932e93d5a909e..7af6e43e1db8b0 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -469,7 +469,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_syntax.rb b/test/ruby/test_syntax.rb index b0ad012131208c..7a5f4c9528762f 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 From f0ce1186620273a1182e6084559765143099eb88 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Thu, 17 Nov 2022 23:43:21 +0900 Subject: [PATCH 57/77] Make anonymous rest arg (*) and block arg (&) accessible from ARGS node --- node.h | 1 + parse.y | 11 +++++++---- test/ruby/test_ast.rb | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) 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/parse.y b/parse.y index f5f8e15acf3ba6..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; + /*% %*/ } ; @@ -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) %*/ } @@ -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); diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 7af6e43e1db8b0..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") From 10788166e7e568fdcd0b748e8d5dab442dcdc7ef Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Nov 2022 15:57:11 -0800 Subject: [PATCH 58/77] Differentiate T_OBJECT shapes from other objects We would like to differentiate types of objects via their shape. This commit adds a special T_OBJECT shape when we allocate an instance of T_OBJECT. This allows us to avoid testing whether an object is an instance of a T_OBJECT or not, we can just check the shape. --- gc.c | 8 +++++++- object.c | 2 +- shape.c | 23 +++++++++++++++++++++-- shape.h | 3 ++- test/ruby/test_shapes.rb | 19 ++++++++++++++----- variable.c | 1 + 6 files changed, 46 insertions(+), 10 deletions(-) diff --git a/gc.c b/gc.c index 5a5ec869ca0e56..06015e38801cde 100644 --- a/gc.c +++ b/gc.c @@ -2942,6 +2942,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); @@ -10030,7 +10036,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); } diff --git a/object.c b/object.c index 7f4404c46217b1..e50430a4fbe50e 100644 --- a/object.c +++ b/object.c @@ -293,7 +293,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); } diff --git a/shape.c b/shape.c index bb51abecbf609e..d98e596eb1fe83 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"); @@ -333,14 +336,16 @@ 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) { + 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 @@ -355,6 +360,8 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape) case SHAPE_ROOT: case SHAPE_FROZEN: case SHAPE_CAPACITY_CHANGE: + case SHAPE_INITIAL_CAPACITY: + case SHAPE_T_OBJECT: break; } @@ -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,6 +662,7 @@ 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)); diff --git a/shape.h b/shape.h index a2cbd7c482ee86..9bbc5d35f9343a 100644 --- a/shape.h +++ b/shape.h @@ -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 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/variable.c b/variable.c index daae90396e1fb3..15e18f21aef084 100644 --- a/variable.c +++ b/variable.c @@ -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; } From b1cbc883f2add06479113b61005f4cdfa90ff266 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 18 Nov 2022 09:34:38 -0800 Subject: [PATCH 59/77] [ruby/irb] Minor fixes on debug command (https://github.com/ruby/irb/pull/447) * Minor fixes on debug command * Update lib/irb/cmd/debug.rb --- lib/irb.rb | 9 +++--- lib/irb/cmd/debug.rb | 70 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/lib/irb.rb b/lib/irb.rb index 1cd89d295cc255..64da852157588c 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -393,8 +393,6 @@ def IRB.irb_abort(irb, exception = Abort) end class Irb - DIR_NAME = __dir__ - ASSIGNMENT_NODE_TYPES = [ # Local, instance, global, class, constant, instance, and index assignment: # "foo = bar", @@ -436,13 +434,14 @@ 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?(:original_capture_frames) + 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, :original_capture_frames) + DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :capture_frames_without_irb) # and remove the redundant method - DEBUGGER__.singleton_class.send(:undef_method, :original_capture_frames) + DEBUGGER__.singleton_class.send(:undef_method, :capture_frames_without_irb) end end diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb index 8aab40cf84afb5..369c112257273d 100644 --- a/lib/irb/cmd/debug.rb +++ b/lib/irb/cmd/debug.rb @@ -5,28 +5,68 @@ module IRB 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) - require "debug/session" - DEBUGGER__.start(nonstop: true) - DEBUGGER__.singleton_class.send(:alias_method, :original_capture_frames, :capture_frames) - - def DEBUGGER__.capture_frames(skip_path_prefix) - frames = original_capture_frames(skip_path_prefix) - frames.reject! do |frame| - frame.realpath&.start_with?(::IRB::Irb::DIR_NAME) || frame.path.match?(/internal:prelude/) - end - frames + 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 - rescue LoadError => e - 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 + 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 + return false + 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 end end From ba3b40a9aeb752d1b3e4c87748c977bfd3cf6f2c Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 18 Nov 2022 10:03:29 -0800 Subject: [PATCH 60/77] [ruby/irb] Discover and load debug.gem even if it's not in Gemfile (https://github.com/ruby/irb/pull/448) * Minor fixes on debug command * Discover and load debug.gem even if it's not in Gemfile * Eliminate else for rescue * Discover the latest one from all gem paths --- lib/irb/cmd/debug.rb | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb index 369c112257273d..d43e060c67a2df 100644 --- a/lib/irb/cmd/debug.rb +++ b/lib/irb/cmd/debug.rb @@ -48,8 +48,8 @@ def setup_debugger unless defined?(DEBUGGER__::SESSION) begin require "debug/session" - rescue LoadError - return false + rescue LoadError # debug.gem is not written in Gemfile + return false unless load_bundled_debug_gem end DEBUGGER__.start(nonstop: true) end @@ -68,6 +68,29 @@ def DEBUGGER__.capture_frames(*args) 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 From 6582f34831cc665b2adcf7d475aceb9b918badb6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 18 Nov 2022 10:29:41 -0800 Subject: [PATCH 61/77] rename SHAPE_BITS to SHAPE_ID_NUM_BITS --- mjit_c.rb | 8 ++++---- ractor_core.h | 2 +- shape.c | 2 +- shape.h | 14 +++++++------- tool/mjit/bindgen.rb | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mjit_c.rb b/mjit_c.rb index da2bab98865b0c..c240a5ec4b9ca8 100644 --- a/mjit_c.rb +++ b/mjit_c.rb @@ -158,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 @@ -174,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 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/shape.c b/shape.c index d98e596eb1fe83..919ae7c433a8fc 100644 --- a/shape.c +++ b/shape.c @@ -665,7 +665,7 @@ Init_shape(void) 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 9bbc5d35f9343a..28b2ce55f0fd28 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 diff --git a/tool/mjit/bindgen.rb b/tool/mjit/bindgen.rb index 688d48ccb2f537..c1003fd723e4b4 100755 --- a/tool/mjit/bindgen.rb +++ b/tool/mjit/bindgen.rb @@ -346,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 From 9e067df76bda7384e8f3ddda2b348f3a742eb784 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 15 Nov 2022 11:46:18 -0800 Subject: [PATCH 62/77] 32 bit comparison on shape id This commit changes the shape id comparisons to use a 32 bit comparison rather than 64 bit. That means we don't need to load the shape id to a register on x86 machines. Given the following program: ```ruby class Foo def initialize @foo = 1 @bar = 1 end def read [@foo, @bar] end end foo = Foo.new foo.read foo.read foo.read foo.read foo.read puts RubyVM::YJIT.disasm(Foo.instance_method(:read)) ``` The machine code we generated _before_ this change is like this: ``` == BLOCK 1/4, ISEQ RANGE [0,3), 65 bytes ====================== # getinstancevariable 0x559a18623023: mov rax, qword ptr [r13 + 0x18] # guard object is heap 0x559a18623027: test al, 7 0x559a1862302a: jne 0x559a1862502d 0x559a18623030: cmp rax, 4 0x559a18623034: jbe 0x559a1862502d # guard shape, embedded, and T_OBJECT 0x559a1862303a: mov rcx, qword ptr [rax] 0x559a1862303d: movabs r11, 0xffff00000000201f 0x559a18623047: and rcx, r11 0x559a1862304a: movabs r11, 0xb000000002001 0x559a18623054: cmp rcx, r11 0x559a18623057: jne 0x559a18625046 0x559a1862305d: mov rax, qword ptr [rax + 0x18] 0x559a18623061: mov qword ptr [rbx], rax == BLOCK 2/4, ISEQ RANGE [3,6), 0 bytes ======================= == BLOCK 3/4, ISEQ RANGE [3,6), 47 bytes ====================== # gen_direct_jmp: fallthrough # getinstancevariable # regenerate_branch # getinstancevariable # regenerate_branch 0x559a18623064: mov rax, qword ptr [r13 + 0x18] # guard shape, embedded, and T_OBJECT 0x559a18623068: mov rcx, qword ptr [rax] 0x559a1862306b: movabs r11, 0xffff00000000201f 0x559a18623075: and rcx, r11 0x559a18623078: movabs r11, 0xb000000002001 0x559a18623082: cmp rcx, r11 0x559a18623085: jne 0x559a18625099 0x559a1862308b: mov rax, qword ptr [rax + 0x20] 0x559a1862308f: mov qword ptr [rbx + 8], rax ``` After this change, it's like this: ``` == BLOCK 1/4, ISEQ RANGE [0,3), 41 bytes ====================== # getinstancevariable 0x5560c986d023: mov rax, qword ptr [r13 + 0x18] # guard object is heap 0x5560c986d027: test al, 7 0x5560c986d02a: jne 0x5560c986f02d 0x5560c986d030: cmp rax, 4 0x5560c986d034: jbe 0x5560c986f02d # guard shape 0x5560c986d03a: cmp word ptr [rax + 6], 0x19 0x5560c986d03f: jne 0x5560c986f046 0x5560c986d045: mov rax, qword ptr [rax + 0x10] 0x5560c986d049: mov qword ptr [rbx], rax == BLOCK 2/4, ISEQ RANGE [3,6), 0 bytes ======================= == BLOCK 3/4, ISEQ RANGE [3,6), 23 bytes ====================== # gen_direct_jmp: fallthrough # getinstancevariable # regenerate_branch # getinstancevariable # regenerate_branch 0x5560c986d04c: mov rax, qword ptr [r13 + 0x18] # guard shape 0x5560c986d050: cmp word ptr [rax + 6], 0x19 0x5560c986d055: jne 0x5560c986f099 0x5560c986d05b: mov rax, qword ptr [rax + 0x18] 0x5560c986d05f: mov qword ptr [rbx + 8], rax ``` The first ivar read is a bit more complex, but the second ivar read is much simpler. I think eventually we could teach the context about the shape, then emit only one shape guard. --- shape.c | 6 +++--- shape.h | 1 + yjit/bindgen/src/main.rs | 2 +- yjit/src/asm/arm64/mod.rs | 2 -- yjit/src/backend/arm64/mod.rs | 19 ++++++++++++++++++- yjit/src/backend/ir.rs | 6 +++--- yjit/src/codegen.rs | 15 ++++++--------- yjit/src/cruby_bindings.inc.rs | 6 +++--- 8 files changed, 35 insertions(+), 22 deletions(-) diff --git a/shape.c b/shape.c index 919ae7c433a8fc..430bfeb90a4f49 100644 --- a/shape.c +++ b/shape.c @@ -325,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 * diff --git a/shape.h b/shape.h index 28b2ce55f0fd28..43308b833baf20 100644 --- a/shape.h +++ b/shape.h @@ -132,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/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/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/codegen.rs b/yjit/src/codegen.rs index be4fc423b6f47c..c66a29332228cd 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -2040,16 +2040,13 @@ 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; + 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)); - // Combined guard for all flags: shape, embeddedness, and T_OBJECT - let flags_opnd = Opnd::mem(64, recv, RUBY_OFFSET_RBASIC_FLAGS); - - 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, 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; From 2185f0ca772c1e8c318198b54e3a694b55945bbb Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 18 Nov 2022 13:58:13 -0800 Subject: [PATCH 63/77] Update assertion New T_OBJECT objects will have a T_OBJECT shape --- object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object.c b/object.c index e50430a4fbe50e..eae5a4308874cf 100644 --- a/object.c +++ b/object.c @@ -306,7 +306,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); } From 7f269a3c2d19e28d493a492748cd998d4cd43c89 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Fri, 18 Nov 2022 14:59:04 -0500 Subject: [PATCH 64/77] Fix io/console test for --with-static-linked-ext The tests looks for the .so file, which doesn't exist when the extension is statically linked. In that situation it passes -I to ruby with nothing after it which ate the -rio/console argument. --- test/io/console/test_io_console.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 From 5c505f4a48cafd83493ce2744373bd33361ab9f1 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Fri, 18 Nov 2022 19:42:24 -0500 Subject: [PATCH 65/77] Rename misleading label We use this code path when we require the same extension twice, so it's not necessarily about the feature being statically linked. Removing this code when there is no statically linked extensions leads to breakage. --- load.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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; } From 082cfcfd06f85ea5c578f0654431dad5c2f32814 Mon Sep 17 00:00:00 2001 From: yui-knk Date: Fri, 18 Nov 2022 19:42:37 +0900 Subject: [PATCH 66/77] Add test cases for args forwarding after rest argument --- test/ruby/test_syntax.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 7a5f4c9528762f..251448ec011615 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -1709,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) From 6dcb7b92169e5bd490bb6eaa4d5b9b7445af464c Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 18 Nov 2022 17:27:07 -0800 Subject: [PATCH 67/77] YJIT: Improve the failure message on enlarging a branch (#6769) --- yjit/src/core.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/yjit/src/core.rs b/yjit/src/core.rs index 78b4cbdb0c91a9..869c0859b5e869 100644 --- a/yjit/src/core.rs +++ b/yjit/src/core.rs @@ -1851,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 @@ -2258,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() + ); } } From 574d849869032f987d6b9168e01fe704258b8974 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Fri, 18 Nov 2022 18:56:27 -0500 Subject: [PATCH 68/77] Run skipped minitest tests that now pass The mentioned PR was merged. --- tool/test-bundled-gems.rb | 5 ----- 1 file changed, 5 deletions(-) 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 From 50c6cabadca44b7b034eae5dcc8017154a2858bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciek=20Rz=C4=85sa?= Date: Sat, 19 Nov 2022 03:38:16 +0100 Subject: [PATCH 69/77] [DOC] Change formatting in the exec docs The last part of the sentence was accidentally put in enumeration, It made an impression that it's one of the rules, not the result of applying the rules. --- process.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/process.c b/process.c index dabe9320f297fe..dbe2be5b56fbe3 100644 --- a/process.c +++ b/process.c @@ -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). From 951eabdcf2f0a8c8b0f23904b65bbaf93cde8d82 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Tue, 15 Nov 2022 18:07:46 +0000 Subject: [PATCH 70/77] [ruby/net-http] Enhanced RDoc for Net::HTTP https://github.com/ruby/net-http/commit/6b30c5310b --- lib/net/http.rb | 675 +++++++++++++++++++++------------------ lib/net/http/request.rb | 83 ++++- lib/net/http/response.rb | 158 ++++++++- 3 files changed, 596 insertions(+), 320 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 16137cebf22d12..535ca10a0e0653 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -32,110 +32,290 @@ 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 + # + # {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 + # + # Each of the following methods automatically starts and finishes + # a {session}[rdoc-ref:Net::HTTP@Sessions] that sends a single request: # - # 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). + # # Return string response body. + # Net::HTTP.get(hostname, path, port = 80) + # Net::HTTP.get(uri, headers = {}, port = 80) # - # 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. + # # Write string response body to $stdout. + # Net::HTTP.get_print(hostname, path_or_uri, port = 80) + # Net::HTTP.get_print(uri, headers = {}, port = 80) # - # If you are only performing a few GET requests you should try OpenURI. + # # Return response as Net::HTTPResponse object. + # Net::HTTP.get_response(hostname, path_or_uri, port = 80) + # Net::HTTP.get_response(uri, headers = {}, port = 80) # - # == Simple Examples + # Net::HTTP.post(uri, data, headers = {}) + # Net::HTTP.post_form(uri, params) # - # All examples assume you have loaded Net::HTTP with: + # == About the Examples + # + # Examples here assume that net/http has been required + # (which also requires +uri+): # # require 'net/http' # - # This will also require 'uri' so you don't need to require it separately. + # Many code examples here use these example websites: # - # The Net::HTTP methods in the following section do not persist - # connections. They are not recommended if you are performing many HTTP - # requests. + # - https://jsonplaceholder.typicode.com. + # - http:example.com. # - # === GET + # Some examples also assume these variables: # - # Net::HTTP.get('example.com', '/index.html') # => String + # uri = URI('https://jsonplaceholder.typicode.com') + # uri.freeze # Examples may not modify. + # hostname = uri.hostname # => "jsonplaceholder.typicode.com" + # port = uri.port # => 443 # - # === GET by URI + # So that example requests may be written as: # - # uri = URI('http://example.com/index.html?count=10') - # Net::HTTP.get(uri) # => String + # 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 # - # === GET with Dynamic Parameters + # An example that needs a modified URI first duplicates +uri+, then modifies: # - # uri = URI('http://example.com/index.html') - # params = { :limit => 10, :page => 3 } - # uri.query = URI.encode_www_form(params) + # _uri = uri.dup + # _uri.path = '/todos/1' # - # res = Net::HTTP.get_response(uri) - # puts res.body if res.is_a?(Net::HTTPSuccess) + # == URIs # - # === POST + # 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]. # - # uri = URI('http://www.example.com/search.cgi') - # res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50') - # puts res.body + # A Ruby {URI::Generic}[]https://docs.ruby-lang.org/en/master/URI/Generic.html] object + # represents an internet URI. + # It provides, among others, methods + # +scheme+, +hostname+, +path+, +query+, and +fragment+. # - # === POST with Multiple Values + # === Schemes # - # uri = URI('http://www.example.com/search.cgi') - # res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50') - # puts res.body + # An internet \URI has + # a {scheme}[https://en.wikipedia.org/wiki/List_of_URI_schemes]. # - # == How to use Net::HTTP + # The two schemes supported in \Net::HTTP are 'https' and 'http': # - # 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.scheme # => "https" + # URI('http://example.com').scheme # => "http" # - # uri = URI('http://example.com/some_path?query=string') + # === Hostnames # - # Net::HTTP.start(uri.host, uri.port) do |http| - # request = Net::HTTP::Get.new uri + # A hostname identifies a server (host) to which requests may be sent: # - # response = http.request request # Net::HTTPResponse object + # hostname = uri.hostname # => "jsonplaceholder.typicode.com" + # Net::HTTP.start(hostname) do |http| + # # Some HTTP stuff. # end # - # 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. + # === Paths # - # 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 host-specific path identifies a resource on the host: # - # The request types Net::HTTP supports are listed below in the section "HTTP - # Request Classes". + # _uri = uri.dup + # _uri.path = '/todos/1' + # hostname = _uri.hostname + # path = _uri.path + # Net::HTTP.get(hostname, path) # - # 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. + # === Queries # - # === Response Data + # A host-specific query adds name/value pairs to the URI: # - # uri = URI('http://example.com/index.html') - # res = Net::HTTP.get_response(uri) + # _uri = uri.dup + # params = {userId: 1, completed: false} + # _uri.query = URI.encode_www_form(params) + # _uri # => # + # Net::HTTP.get(_uri) # - # # Headers - # res['Set-Cookie'] # => String - # res.get_fields('set-cookie') # => Array - # res.to_hash['set-cookie'] # => Array - # puts "Headers: #{res.to_hash.inspect}" + # === Fragments # - # # Status - # puts res.code # => '200' - # puts res.message # => 'OK' - # puts res.class.name # => 'HTTPOK' + # 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. # - # # Body - # puts res.body + # == Request Headers + # + # 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. + # + # 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: + # + # headers = {Accept: 'application/json', Connection: 'Keep-Alive'} + # Net::HTTP.get(uri, headers) + # + # 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. + # + # The following example performs a conditional GET using the + # If-Modified-Since header: + # + # - If the file +cached_response+ has been modified since the time + # put into the header, + # the return is a \Net::HTTPSuccess object, + # and the file is overwritten with the response body. + # - Otherwise, the return is a \Net::HTTPNotModified object, + # and the file remains unchanged. + # + # The code: + # + # path = 'cached_response' + # File.write(path, '') unless File.exist?(path) + # file = File.stat(path) + # req = Net::HTTP::Get.new(uri) + # req['If-Modified-Since'] = file.mtime.rfc2822 + # res = Net::HTTP.start(hostname) do |http| + # http.request(req) + # end + # if res.is_a?(Net::HTTPSuccess) + # File.write(path, res.body) + # end + # + # == Sessions + # + # 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. + # + # === 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 + # == Following Redirection # # Each Net::HTTPResponse object belongs to a class for its response code. # @@ -167,56 +347,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 +362,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 +382,7 @@ class HTTPHeaderSyntaxError < StandardError; end # end # end # - # === HTTPS + # == HTTPS # # HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=. # @@ -272,7 +403,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 +421,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 +429,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 +443,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 +463,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 = 80) -> 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 +478,54 @@ 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: - # - # print Net::HTTP.get(URI('http://www.example.com/index.html')) + # :call-seq: + # Net::HTTP.get(hostname, path, port = 80) -> body + # Net::HTTP:get(uri, headers = {}, port = 80) -> body # - # or: + # Sends a GET request and returns the \HTTP response body as a string. # - # print Net::HTTP.get('www.example.com', '/index.html') + # With string arguments +hostname+ and +path+: # - # you can also specify request headers: + # hostname = 'jsonplaceholder.typicode.com' + # path = '/todos/1' + # puts Net::HTTP.get(hostname, path) # - # Net::HTTP.get(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }) + # Output: # - 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: + # { + # "userId": 1, + # "id": 1, + # "title": "delectus aut autem", + # "completed": + # } # - # res = Net::HTTP.get_response(URI('http://www.example.com/index.html')) - # print res.body + # With URI object +uri+ and optional hash argument +headers+: # - # or: + # uri = URI('https://jsonplaceholder.typicode.com/todos/1') + # headers = {Accept: 'text/html'} + # puts Net::HTTP.get(uri, headers) # - # res = Net::HTTP.get_response('www.example.com', '/index.html') - # print res.body + # Output: # - # you can also specify request headers: + # { + # "userId": 1, + # "id": 1, + # "title": "delectus aut autem", + # "completed": false + # } # - # Net::HTTP.get_response(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }) + # In either case, the third argument is an integer port number, + # which defaults to 80. + def HTTP.get(uri_or_host, path_or_headers = nil, port = nil) + get_response(uri_or_host, path_or_headers, port).body + end + + # :call-seq: + # Net::HTTP.get_response(hostname, path, port = 80) -> http_response + # Net::HTTP:get_response(uri, headers = {}, port = 80) -> 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 @@ -588,36 +611,78 @@ def HTTP.socket_type #:nodoc: obsolete BufferedIO 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. + # 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..55099019d65c3a 100644 --- a/lib/net/http/request.rb +++ b/lib/net/http/request.rb @@ -1,8 +1,83 @@ # 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, which are covered in the sections below. +# +# == About the Examples +# +# 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 +# +# An example that needs a modified URI first duplicates +uri+, then modifies: +# +# _uri = uri.dup +# _uri.path = '/todos/1' +# +# == Requests +# +# === \Net::HTTP::Get +# +# A GET request may be sent using request class \Net::HTTP::Get: +# +# req = Net::HTTP::Get.new(uri) # => # +# Net::HTTP.start(hostname) do |http| +# http.request(req) +# end # => # +# +# === \Net::HTTP::Head +# +# A HEAD request may be sent using request class \Net::HTTP::Head: +# +# req = Net::HTTP::Head.new(uri) # => # +# Net::HTTP.start(hostname) do |http| +# http.request(req) +# end # => # +# +# === \Net::HTTP::Post +# +# A POST request may be sent using request class \Net::HTTP::Post: +# +# require 'json' +# json = JSON.generate({title: 'foo', body: 'bar', userId: 1}) +# # => "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}" +# _uri = uri.dup +# _uri.path = '/posts' +# req = Net::HTTP::Post.new(_uri) # => # +# req.body = json +# req['Content-type'] = 'application/json; charset=UTF-8' +# Net::HTTP.start(hostname) do |http| +# http.request(req) +# end # => # => # +# +# === \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 # 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..043672d3e21681 100644 --- a/lib/net/http/response.rb +++ b/lib/net/http/response.rb @@ -1,20 +1,156 @@ # frozen_string_literal: false -# HTTP response class. + +# This class is the base class for \Net::HTTP request classes. +# +# == About the Examples +# +# 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 +# +# An example that needs a modified URI first duplicates +uri+, then modifies: +# +# _uri = uri.dup +# _uri.path = '/todos/1' +# +# == 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) +# +# - 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 From dbb902fc91b8b9a298d8656830f724c767345969 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Wed, 16 Nov 2022 17:39:17 +0000 Subject: [PATCH 71/77] [ruby/net-http] Enhanced RDoc for Net::HTTP https://github.com/ruby/net-http/commit/4444e8cea4 --- lib/net/http.rb | 54 ++++++++-------------------------------- lib/net/http/request.rb | 5 ++-- lib/net/http/response.rb | 4 +-- 3 files changed, 15 insertions(+), 48 deletions(-) diff --git a/lib/net/http.rb b/lib/net/http.rb index 535ca10a0e0653..7fd6f9d1b3b3f6 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -106,7 +106,7 @@ class HTTPHeaderSyntaxError < StandardError; end # Many code examples here use these example websites: # # - https://jsonplaceholder.typicode.com. - # - http:example.com. + # - http://example.com. # # Some examples also assume these variables: # @@ -137,7 +137,7 @@ class HTTPHeaderSyntaxError < StandardError; end # 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]. # - # A Ruby {URI::Generic}[]https://docs.ruby-lang.org/en/master/URI/Generic.html] object + # A Ruby {URI::Generic}[rdoc-ref:URI::Generic] object # represents an internet URI. # It provides, among others, methods # +scheme+, +hostname+, +path+, +query+, and +fragment+. @@ -204,30 +204,6 @@ class HTTPHeaderSyntaxError < StandardError; end # {Request Fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields]. # A host may also accept other custom fields. # - # The following example performs a conditional GET using the - # If-Modified-Since header: - # - # - If the file +cached_response+ has been modified since the time - # put into the header, - # the return is a \Net::HTTPSuccess object, - # and the file is overwritten with the response body. - # - Otherwise, the return is a \Net::HTTPNotModified object, - # and the file remains unchanged. - # - # The code: - # - # path = 'cached_response' - # File.write(path, '') unless File.exist?(path) - # file = File.stat(path) - # req = Net::HTTP::Get.new(uri) - # req['If-Modified-Since'] = file.mtime.rfc2822 - # res = Net::HTTP.start(hostname) do |http| - # http.request(req) - # end - # if res.is_a?(Net::HTTPSuccess) - # File.write(path, res.body) - # end - # # == Sessions # # A _session_ is a connection between a server (host) and a client that: @@ -465,7 +441,7 @@ class << HTTP # :call-seq: # Net::HTTP.get_print(hostname, path, port = 80) -> nil - # Net::HTTP:get_print(uri, headers = {}, port = 80) -> nil + # Net::HTTP:get_print(uri, headers = {}, port = uri.port) -> nil # # Like Net::HTTP.get, but writes the returned body to $stdout; # returns +nil+. @@ -480,7 +456,7 @@ def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil) # :call-seq: # Net::HTTP.get(hostname, path, port = 80) -> body - # Net::HTTP:get(uri, headers = {}, port = 80) -> body + # Net::HTTP:get(uri, headers = {}, port = uri.port) -> body # # Sends a GET request and returns the \HTTP response body as a string. # @@ -496,33 +472,22 @@ def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil) # "userId": 1, # "id": 1, # "title": "delectus aut autem", - # "completed": + # "completed": false # } # # With URI object +uri+ and optional hash argument +headers+: # # uri = URI('https://jsonplaceholder.typicode.com/todos/1') - # headers = {Accept: 'text/html'} - # puts Net::HTTP.get(uri, headers) + # headers = {'Content-type' => 'application/json; charset=UTF-8'} + # Net::HTTP.get(uri, headers) # - # Output: - # - # { - # "userId": 1, - # "id": 1, - # "title": "delectus aut autem", - # "completed": false - # } - # - # In either case, the third argument is an integer port number, - # which defaults to 80. def HTTP.get(uri_or_host, path_or_headers = nil, port = nil) get_response(uri_or_host, path_or_headers, port).body end # :call-seq: # Net::HTTP.get_response(hostname, path, port = 80) -> http_response - # Net::HTTP:get_response(uri, headers = {}, 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. @@ -611,6 +576,9 @@ def HTTP.socket_type #:nodoc: obsolete BufferedIO end + # :call-seq: + # 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. # diff --git a/lib/net/http/request.rb b/lib/net/http/request.rb index 55099019d65c3a..1a6e5884950e05 100644 --- a/lib/net/http/request.rb +++ b/lib/net/http/request.rb @@ -16,7 +16,7 @@ # Many code examples here use these example websites: # # - https://jsonplaceholder.typicode.com. -# - http:example.com. +# - http://example.com. # # Some examples also assume these variables: # @@ -54,8 +54,7 @@ # # A POST request may be sent using request class \Net::HTTP::Post: # -# require 'json' -# json = JSON.generate({title: 'foo', body: 'bar', userId: 1}) +# json = {title: 'foo', body: 'bar', userId: 1} # # => "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}" # _uri = uri.dup # _uri.path = '/posts' diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb index 043672d3e21681..567b9573fa0574 100644 --- a/lib/net/http/response.rb +++ b/lib/net/http/response.rb @@ -12,7 +12,7 @@ # Many code examples here use these example websites: # # - https://jsonplaceholder.typicode.com. -# - http:example.com. +# - http://example.com. # # Some examples also assume these variables: # @@ -94,7 +94,7 @@ # - Net::HTTPAlreadyReported (208) # - Net::HTTPIMUsed (226) # -# - HTTPRedirection: +# - Net::HTTPRedirection: # # - Net::HTTPMultipleChoices (300) # - Net::HTTPMovedPermanently (301) From 2047636af81d08d20a083fdd08cc771d772eecfb Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Wed, 16 Nov 2022 13:33:19 -0600 Subject: [PATCH 72/77] [ruby/net-http] Update lib/net/http/request.rb https://github.com/ruby/net-http/commit/e3c9011edb Co-authored-by: Peter Zhu --- lib/net/http/request.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/net/http/request.rb b/lib/net/http/request.rb index 1a6e5884950e05..f61697d1a80a93 100644 --- a/lib/net/http/request.rb +++ b/lib/net/http/request.rb @@ -54,12 +54,10 @@ # # A POST request may be sent using request class \Net::HTTP::Post: # -# json = {title: 'foo', body: 'bar', userId: 1} -# # => "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}" # _uri = uri.dup # _uri.path = '/posts' # req = Net::HTTP::Post.new(_uri) # => # -# req.body = json +# req.body = '{"title": "foo", "body": "bar", "userId": 1}' # req['Content-type'] = 'application/json; charset=UTF-8' # Net::HTTP.start(hostname) do |http| # http.request(req) From e6162728f6ca72e97805c7ee16b98f6639b2af11 Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 18 Nov 2022 14:10:38 +0000 Subject: [PATCH 73/77] [ruby/net-http] Enhanced RDoc for Net::HTTP --- lib/net/http/request.rb | 88 ++++++++--------------------------------- 1 file changed, 17 insertions(+), 71 deletions(-) diff --git a/lib/net/http/request.rb b/lib/net/http/request.rb index f61697d1a80a93..279316e0b9e332 100644 --- a/lib/net/http/request.rb +++ b/lib/net/http/request.rb @@ -4,77 +4,23 @@ # it wraps together the request path and the request headers. # # The class should not be used directly; -# instead you should use its subclasses, which are covered in the sections below. -# -# == About the Examples -# -# 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 -# -# An example that needs a modified URI first duplicates +uri+, then modifies: -# -# _uri = uri.dup -# _uri.path = '/todos/1' -# -# == Requests -# -# === \Net::HTTP::Get -# -# A GET request may be sent using request class \Net::HTTP::Get: -# -# req = Net::HTTP::Get.new(uri) # => # -# Net::HTTP.start(hostname) do |http| -# http.request(req) -# end # => # -# -# === \Net::HTTP::Head -# -# A HEAD request may be sent using request class \Net::HTTP::Head: -# -# req = Net::HTTP::Head.new(uri) # => # -# Net::HTTP.start(hostname) do |http| -# http.request(req) -# end # => # -# -# === \Net::HTTP::Post -# -# A POST request may be sent using request class \Net::HTTP::Post: -# -# _uri = uri.dup -# _uri.path = '/posts' -# req = Net::HTTP::Post.new(_uri) # => # -# req.body = '{"title": "foo", "body": "bar", "userId": 1}' -# req['Content-type'] = 'application/json; charset=UTF-8' -# Net::HTTP.start(hostname) do |http| -# http.request(req) -# end # => # => # -# -# === \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 +# 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+. From 909ea6b60d658b2a1c4a53be81b09642dcf86d4b Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Fri, 18 Nov 2022 22:40:03 +0000 Subject: [PATCH 74/77] [ruby/net-http] About the Examples moved to separate file https://github.com/ruby/net-http/commit/0512b5bfc9 --- doc/net-http/examples.rdoc | 30 ++++++++++++++++++++++++++++++ lib/net/http.rb | 31 +------------------------------ lib/net/http/response.rb | 22 +--------------------- 3 files changed, 32 insertions(+), 51 deletions(-) create mode 100644 doc/net-http/examples.rdoc 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/lib/net/http.rb b/lib/net/http.rb index 7fd6f9d1b3b3f6..f07586e656031e 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -98,36 +98,7 @@ class HTTPHeaderSyntaxError < StandardError; end # # == About the Examples # - # 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: - # - # _uri = uri.dup - # _uri.path = '/todos/1' + # :include: doc/net-http/examples.rdoc # # == URIs # diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb index 567b9573fa0574..83853ffd1942de 100644 --- a/lib/net/http/response.rb +++ b/lib/net/http/response.rb @@ -4,27 +4,7 @@ # # == About the Examples # -# 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 -# -# An example that needs a modified URI first duplicates +uri+, then modifies: -# -# _uri = uri.dup -# _uri.path = '/todos/1' +# :include: doc/net-http/examples.rdoc # # == Returned Responses # From 01bc2fc514c732406ad39d63b2e97b76f2f56de4 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 20 Nov 2022 01:10:40 +0900 Subject: [PATCH 75/77] Update fake.rb for test-spec spec/ruby/command_line/dash_v_spec.rb needs it. --- common.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.mk b/common.mk index fbcb3c3551aaf2..056d3d4cd2cb63 100644 --- a/common.mk +++ b/common.mk @@ -891,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 From 23750c866a69cec982cc16921bbdc6224dfded94 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 19 Nov 2022 14:10:05 -0800 Subject: [PATCH 76/77] [ruby/irb] Update documentation about Autocompletion (https://github.com/ruby/irb/pull/452) https://github.com/ruby/irb/commit/e6b4917750 --- lib/irb.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/irb.rb b/lib/irb.rb index 64da852157588c..741ad7e59f9b0c 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -93,9 +93,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 # From 5958c305e527460465bdbd43e59b2da26d4cfbfb Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 19 Nov 2022 14:14:40 -0800 Subject: [PATCH 77/77] [ruby/irb] Document a full list of commands (https://github.com/ruby/irb/pull/451) * Document a full list of commands * Document debug as well * Make it less duplicated --- README.md | 44 +++++++++++++++++++++++++++++++++++++++ lib/irb.rb | 44 +++++++++++++++++++++++++++++++++++++++ lib/irb/extend-command.rb | 21 +------------------ 3 files changed, 89 insertions(+), 20 deletions(-) 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/lib/irb.rb b/lib/irb.rb index 741ad7e59f9b0c..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. diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index 7da75fe1477d90..94fd9c8bb4fab6 100644 --- a/lib/irb/extend-command.rb +++ b/lib/irb/extend-command.rb @@ -165,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)