diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index 4e574b6be167f8..376163fcdc11a7 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -98,7 +98,7 @@ jobs: rustup install ${{ matrix.rust_version }} --profile minimal rustup default ${{ matrix.rust_version }} - - uses: taiki-e/install-action@b6b84cf49ebfe0176417bdce007c624f0db37f20 # v2.86.2 + - uses: taiki-e/install-action@5b4d68e2e660441203ab128a23676f1e4faf1532 # v2.86.3 with: tool: nextest@0.9 if: ${{ matrix.test_task == 'zjit-check' }} diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index 2f323a61f4a78b..4fb0bd5100345b 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -152,7 +152,7 @@ jobs: ruby-version: '3.1' bundler: none - - uses: taiki-e/install-action@b6b84cf49ebfe0176417bdce007c624f0db37f20 # v2.86.2 + - uses: taiki-e/install-action@5b4d68e2e660441203ab128a23676f1e4faf1532 # v2.86.3 with: tool: nextest@0.9 if: ${{ matrix.test_task == 'zjit-check' }} diff --git a/NEWS.md b/NEWS.md index c3599f0dbcbef4..064327addcbb12 100644 --- a/NEWS.md +++ b/NEWS.md @@ -179,7 +179,7 @@ They are still available on rubygems.org and can be installed with * 0.6.3 to [v0.6.4][pp-v0.6.4] * prism 1.9.0 * 1.7.0 to [v1.8.0][prism-v1.8.0], [v1.8.1][prism-v1.8.1], [v1.9.0][prism-v1.9.0] -* psych 5.4.0 +* psych 5.5.0 * 5.3.1 to [v5.4.0][psych-v5.4.0] * resolv 0.7.1 * 0.7.0 to [v0.7.1][resolv-v0.7.1] diff --git a/compile.c b/compile.c index 5565192ca832aa..13509af55af39f 100644 --- a/compile.c +++ b/compile.c @@ -628,17 +628,28 @@ setup_branch(const rb_code_location_t *loc, const char *type, VALUE structure, V } static VALUE -decl_branch_base(rb_iseq_t *iseq, VALUE key, const rb_code_location_t *loc, const char *type) +decl_branch_base(rb_iseq_t *iseq, int node_id, const rb_code_location_t *loc, const char *type) { if (!branch_coverage_valid_p(iseq, loc->beg_pos.lineno)) return Qundef; /* - * if !structure[node] - * structure[node] = [type, first_lineno, first_column, last_lineno, last_column, branches = {}] + * A branch base is keyed by [source_hash (in two halves), node_id, first_lineno], + * which identifies the branch node stably even across (re-)evals against + * the same path. + * + * if !structure[key] + * structure[key] = [type, first_lineno, first_column, last_lineno, last_column, branches = {}] * else - * branches = structure[node][5] + * branches = structure[key][5] * end */ + uint64_t source_hash = ISEQ_BODY(iseq)->source_hash; + VALUE key = rb_ary_new_from_args(4, + ULONG2NUM((unsigned long)(source_hash >> 32)), + ULONG2NUM((unsigned long)(source_hash & 0xffffffff)), + INT2FIX(node_id), + INT2FIX(loc->beg_pos.lineno)); + rb_ary_freeze(key); VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0); VALUE branch_base = rb_hash_aref(structure, key); @@ -7082,7 +7093,7 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int ADD_SEQ(ret, cond_seq); if (then_label->refcnt && else_label->refcnt) { - branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless"); + branches = decl_branch_base(iseq, nd_node_id(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless"); } if (then_label->refcnt) { @@ -7162,7 +7173,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head)); - branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case"); + branches = decl_branch_base(iseq, nd_node_id(node), nd_code_loc(node), "case"); node = RNODE_CASE(node)->nd_body; EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG); @@ -7267,7 +7278,7 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no VALUE branches = Qfalse; int branch_id = 0; - branches = decl_branch_base(iseq, PTR2NUM(orig_node), nd_code_loc(orig_node), "case"); + branches = decl_branch_base(iseq, nd_node_id(orig_node), nd_code_loc(orig_node), "case"); INIT_ANCHOR(body_seq); endlabel = NEW_LABEL(nd_line(node)); @@ -8266,7 +8277,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no INIT_ANCHOR(body_seq); INIT_ANCHOR(cond_seq); - branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case"); + branches = decl_branch_base(iseq, nd_node_id(node), nd_code_loc(node), "case"); node = RNODE_CASE3(node)->nd_body; EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG); @@ -8470,7 +8481,7 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in if (tmp_label) ADD_LABEL(ret, tmp_label); ADD_LABEL(ret, redo_label); - branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until"); + branches = decl_branch_base(iseq, nd_node_id(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until"); const NODE *const coverage_node = RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node; add_trace_branch_coverage( @@ -9113,7 +9124,7 @@ qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, co LABEL *else_label = NEW_LABEL(nd_line(line_node)); VALUE br = 0; - br = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "&."); + br = decl_branch_base(iseq, nd_node_id(node), nd_code_loc(node), "&."); *branches = br; ADD_INSN(recv, line_node, dup); ADD_INSNL(recv, line_node, branchnil, else_label); diff --git a/ext/psych/lib/psych/versions.rb b/ext/psych/lib/psych/versions.rb index 6c1679bf65bd00..ae73330f2ef5ca 100644 --- a/ext/psych/lib/psych/versions.rb +++ b/ext/psych/lib/psych/versions.rb @@ -2,7 +2,7 @@ module Psych # The version of Psych you are using - VERSION = '5.4.0' + VERSION = '5.5.0' if RUBY_ENGINE == 'jruby' DEFAULT_SNAKEYAML_VERSION = '2.10'.freeze diff --git a/ext/psych/lib/psych/visitors/to_ruby.rb b/ext/psych/lib/psych/visitors/to_ruby.rb index 79565dfcbccfa4..d0bf07071def08 100644 --- a/ext/psych/lib/psych/visitors/to_ruby.rb +++ b/ext/psych/lib/psych/visitors/to_ruby.rb @@ -445,9 +445,6 @@ def deduplicate key end end - def merge_key hash, key, val - end - def revive klass, node s = register(node, klass.allocate) init_with(s, revive_hash({}, node, true), node) diff --git a/ext/psych/lib/psych/visitors/yaml_tree.rb b/ext/psych/lib/psych/visitors/yaml_tree.rb index b6c86f4c94046f..9c2c4aa437e416 100644 --- a/ext/psych/lib/psych/visitors/yaml_tree.rb +++ b/ext/psych/lib/psych/visitors/yaml_tree.rb @@ -496,9 +496,6 @@ def visit_hash_subclass o end end - def dump_list o - end - def dump_exception o, msg tag = ['!ruby/exception', o.class.name].join ':' diff --git a/ext/psych/psych_parser.c b/ext/psych/psych_parser.c index 2729273751fe8c..2b50fcaedd431a 100644 --- a/ext/psych/psych_parser.c +++ b/ext/psych/psych_parser.c @@ -52,19 +52,28 @@ static int io_reader(void * data, unsigned char *buf, size_t size, size_t *read) return 1; } +/* The parser calls back into Ruby for every event, so a handler can call + * Psych::Parser#parse again on the same object. parse() reinitialises the + * parser it is handed, which would pull the input out from under the loop + * still driving it, so keep a flag to reject a reentrant call. */ +typedef struct { + yaml_parser_t yaml_parser; + int parsing; +} psych_parser_t; + static void dealloc(void * ptr) { - yaml_parser_t * parser; + psych_parser_t * parser; - parser = (yaml_parser_t *)ptr; - yaml_parser_delete(parser); + parser = (psych_parser_t *)ptr; + yaml_parser_delete(&parser->yaml_parser); xfree(parser); } #if 0 static size_t memsize(const void *ptr) { - const yaml_parser_t *parser = ptr; + const psych_parser_t *parser = ptr; /* TODO: calculate parser's size */ return 0; } @@ -81,10 +90,10 @@ static const rb_data_type_t psych_parser_type = { static VALUE allocate(VALUE klass) { - yaml_parser_t * parser; - VALUE obj = TypedData_Make_Struct(klass, yaml_parser_t, &psych_parser_type, parser); + psych_parser_t * parser; + VALUE obj = TypedData_Make_Struct(klass, psych_parser_t, &psych_parser_type, parser); - yaml_parser_initialize(parser); + yaml_parser_initialize(&parser->yaml_parser); return obj; } @@ -257,9 +266,22 @@ static VALUE protected_event_location(VALUE pointer) return rb_funcall3(args[0], id_event_location, 4, args + 1); } -static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) +struct parse_args { + psych_parser_t * psych_parser; + VALUE self; + VALUE handler; + VALUE yaml; + VALUE path; +}; + +static VALUE parse_body(VALUE ptr) { - yaml_parser_t * parser; + struct parse_args * pargs = (struct parse_args *)ptr; + yaml_parser_t * parser = &pargs->psych_parser->yaml_parser; + VALUE self = pargs->self; + VALUE handler = pargs->handler; + VALUE yaml = pargs->yaml; + VALUE path = pargs->path; yaml_event_t event; int done = 0; int state = 0; @@ -267,8 +289,6 @@ static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) int encoding = rb_utf8_encindex(); rb_encoding * internal_enc = rb_default_internal_encoding(); - TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser); - yaml_parser_delete(parser); yaml_parser_initialize(parser); @@ -312,6 +332,10 @@ static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) event_args[3] = end_line; event_args[4] = end_column; rb_protect(protected_event_location, (VALUE)event_args, &state); + if (state) { + yaml_event_delete(&event); + rb_jump_tag(state); + } switch(event.type) { case YAML_STREAM_START_EVENT: @@ -496,7 +520,11 @@ static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) rb_protect(protected_end_mapping, handler, &state); break; case YAML_NO_EVENT: + /* Once libyaml has produced the stream end, every later call + * succeeds with a zeroed event and YAML_STREAM_END_EVENT can no + * longer be reached. Stop rather than loop forever. */ rb_protect(protected_empty, handler, &state); + done = 1; break; case YAML_STREAM_END_EVENT: rb_protect(protected_end_stream, handler, &state); @@ -510,6 +538,37 @@ static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) return self; } +static VALUE parse_ensure(VALUE ptr) +{ + psych_parser_t * parser = (psych_parser_t *)ptr; + + parser->parsing = 0; + + return Qnil; +} + +static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) +{ + psych_parser_t * parser; + struct parse_args pargs; + + TypedData_Get_Struct(self, psych_parser_t, &psych_parser_type, parser); + + if (parser->parsing) { + rb_raise(rb_const_get(mPsych, rb_intern("Exception")), + "parser is already parsing, it cannot be reused from a handler callback"); + } + parser->parsing = 1; + + pargs.psych_parser = parser; + pargs.self = self; + pargs.handler = handler; + pargs.yaml = yaml; + pargs.path = path; + + return rb_ensure(parse_body, (VALUE)&pargs, parse_ensure, (VALUE)parser); +} + /* * call-seq: * parser.mark # => # @@ -521,13 +580,13 @@ static VALUE mark(VALUE self) { VALUE mark_klass; VALUE args[3]; - yaml_parser_t * parser; + psych_parser_t * parser; - TypedData_Get_Struct(self, yaml_parser_t, &psych_parser_type, parser); + TypedData_Get_Struct(self, psych_parser_t, &psych_parser_type, parser); mark_klass = rb_const_get_at(cPsychParser, rb_intern("Mark")); - args[0] = SIZET2NUM(parser->mark.index); - args[1] = SIZET2NUM(parser->mark.line); - args[2] = SIZET2NUM(parser->mark.column); + args[0] = SIZET2NUM(parser->yaml_parser.mark.index); + args[1] = SIZET2NUM(parser->yaml_parser.mark.line); + args[2] = SIZET2NUM(parser->yaml_parser.mark.column); return rb_class_new_instance(3, args, mark_klass); } diff --git a/ext/psych/psych_parser_fy.c b/ext/psych/psych_parser_fy.c index 96aa0fe6c25892..ccb05776439372 100644 --- a/ext/psych/psych_parser_fy.c +++ b/ext/psych/psych_parser_fy.c @@ -41,6 +41,11 @@ typedef struct { size_t mark_line; size_t mark_column; size_t mark_index; + /* The parser calls back into Ruby for every event, so a handler can call + * Psych::Parser#parse again on the same object. parse() destroys and + * recreates fyp, which would pull the parser out from under the loop still + * driving it, so keep a flag to reject a reentrant call. */ + int parsing; } psych_fy_parser_t; static const struct fy_parse_cfg psych_parse_cfg = { @@ -256,17 +261,28 @@ static VALUE token_to_str(struct fy_token *tok, int encoding, rb_encoding *inter return str; } -static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) -{ +struct parse_args { psych_fy_parser_t *parser; + VALUE self; + VALUE handler; + VALUE yaml; + VALUE path; +}; + +static VALUE parse_body(VALUE ptr) +{ + struct parse_args *pargs = (struct parse_args *)ptr; + psych_fy_parser_t *parser = pargs->parser; + VALUE self = pargs->self; + VALUE handler = pargs->handler; + VALUE yaml = pargs->yaml; + VALUE path = pargs->path; struct fy_event *event; int done = 0; int state = 0; int encoding = rb_utf8_encindex(); rb_encoding *internal_enc = rb_default_internal_encoding(); - TypedData_Get_Struct(self, psych_fy_parser_t, &psych_parser_type, parser); - /* Use a pristine parser for each parse, like fy-tool does. Reusing a * parser across documents via fy_parser_reset() left the default tag * handles unset for bare (no "---") tag-led documents. */ @@ -348,6 +364,10 @@ static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) event_args[3] = SIZET2NUM(em ? (size_t)em->line : 0); event_args[4] = SIZET2NUM(em ? (size_t)em->column : 0); rb_protect(protected_event_location, (VALUE)event_args, &state); + if (state) { + fy_parser_event_free(parser->fyp, event); + rb_jump_tag(state); + } switch (event->type) { case FYET_STREAM_START: @@ -473,7 +493,10 @@ static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) rb_protect(protected_end_mapping, handler, &state); break; case FYET_NONE: + /* An event with no type cannot advance the stream, so stop + * rather than loop forever. */ rb_protect(protected_empty, handler, &state); + done = 1; break; case FYET_STREAM_END: rb_protect(protected_end_stream, handler, &state); @@ -489,6 +512,37 @@ static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) return self; } +static VALUE parse_ensure(VALUE ptr) +{ + psych_fy_parser_t *parser = (psych_fy_parser_t *)ptr; + + parser->parsing = 0; + + return Qnil; +} + +static VALUE parse(VALUE self, VALUE handler, VALUE yaml, VALUE path) +{ + psych_fy_parser_t *parser; + struct parse_args pargs; + + TypedData_Get_Struct(self, psych_fy_parser_t, &psych_parser_type, parser); + + if (parser->parsing) { + rb_raise(rb_const_get(mPsych, rb_intern("Exception")), + "parser is already parsing, it cannot be reused from a handler callback"); + } + parser->parsing = 1; + + pargs.parser = parser; + pargs.self = self; + pargs.handler = handler; + pargs.yaml = yaml; + pargs.path = path; + + return rb_ensure(parse_body, (VALUE)&pargs, parse_ensure, (VALUE)parser); +} + /* * call-seq: * parser.mark # => # diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb index e8aaf70016bdd1..c52694160800d7 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb @@ -39,76 +39,30 @@ class TimeoutError < ::Gem::Timeout::Error; end # - :auto_reload_after_fork - automatically drop all connections after fork, defaults to true # class Bundler::ConnectionPool - DEFAULTS = {size: 5, timeout: 5, auto_reload_after_fork: true}.freeze - - def self.wrap(options, &block) - Wrapper.new(options, &block) + def self.wrap(**, &) + Wrapper.new(**, &) end - if Process.respond_to?(:fork) - INSTANCES = ObjectSpace::WeakMap.new - private_constant :INSTANCES - - def self.after_fork - INSTANCES.values.each do |pool| - next unless pool.auto_reload_after_fork - - # We're on after fork, so we know all other threads are dead. - # All we need to do is to ensure the main thread doesn't have a - # checked out connection - pool.checkin(force: true) - pool.reload do |connection| - # Unfortunately we don't know what method to call to close the connection, - # so we try the most common one. - connection.close if connection.respond_to?(:close) - end - end - nil - end - - if ::Process.respond_to?(:_fork) # MRI 3.1+ - module ForkTracker - def _fork - pid = super - if pid == 0 - Bundler::ConnectionPool.after_fork - end - pid - end - end - Process.singleton_class.prepend(ForkTracker) - end - else - INSTANCES = nil - private_constant :INSTANCES - - def self.after_fork - # noop - end - end - - def initialize(options = {}, &block) - raise ArgumentError, "Connection pool requires a block" unless block - - options = DEFAULTS.merge(options) + attr_reader :size - @size = Integer(options.fetch(:size)) - @timeout = options.fetch(:timeout) - @auto_reload_after_fork = options.fetch(:auto_reload_after_fork) + def initialize(timeout: 5, size: 5, auto_reload_after_fork: true, name: nil, &) + raise ArgumentError, "Connection pool requires a block" unless block_given? - @available = TimedStack.new(@size, &block) + @size = Integer(size) + @timeout = Float(timeout) + @available = TimedStack.new(size: @size, &) @key = :"pool-#{@available.object_id}" @key_count = :"pool-#{@available.object_id}-count" @discard_key = :"pool-#{@available.object_id}-discard" - INSTANCES[self] = self if @auto_reload_after_fork && INSTANCES + INSTANCES[self] = self if auto_reload_after_fork && INSTANCES end - def with(options = {}) + def with(**) # We need to manage exception handling manually here in order # to work correctly with `Gem::Timeout.timeout` and `Thread#raise`. # Otherwise an interrupted Thread can leak connections. Thread.handle_interrupt(Exception => :never) do - conn = checkout(options) + conn = checkout(**) begin Thread.handle_interrupt(Exception => :immediate) do yield conn @@ -154,13 +108,15 @@ def discard_current_connection(&block) ::Thread.current[@discard_key] = block || proc { |conn| conn } end - def checkout(options = {}) + def checkout(timeout: @timeout, **) if ::Thread.current[@key] ::Thread.current[@key_count] += 1 ::Thread.current[@key] else + conn = @available.pop(timeout:, **) + ::Thread.current[@key] = conn ::Thread.current[@key_count] = 1 - ::Thread.current[@key] = @available.pop(options[:timeout] || @timeout, options) + conn end end @@ -195,29 +151,24 @@ def checkin(force: false) # Shuts down the Bundler::ConnectionPool by passing each connection to +block+ and # then removing it from the pool. Attempting to checkout a connection after # shutdown will raise +Bundler::ConnectionPool::PoolShuttingDownError+. - def shutdown(&block) - @available.shutdown(&block) + def shutdown(&) + @available.shutdown(&) end ## # Reloads the Bundler::ConnectionPool by passing each connection to +block+ and then # removing it the pool. Subsequent checkouts will create new connections as # needed. - def reload(&block) - @available.shutdown(reload: true, &block) + def reload(&) + @available.shutdown(reload: true, &) end ## Reaps idle connections that have been idle for over +idle_seconds+. # +idle_seconds+ defaults to 60. - def reap(idle_seconds = 60, &block) - @available.reap(idle_seconds, &block) + def reap(idle_seconds: 60, &) + @available.reap(idle_seconds:, &) end - # Size of this connection pool - attr_reader :size - # Automatically drop all connections after fork - attr_reader :auto_reload_after_fork - # Number of pool entries available for checkout at this instant. def available @available.length @@ -231,3 +182,4 @@ def idle require_relative "connection_pool/timed_stack" require_relative "connection_pool/wrapper" +require_relative "connection_pool/fork" diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/fork.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/fork.rb new file mode 100644 index 00000000000000..69683d16c26800 --- /dev/null +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/fork.rb @@ -0,0 +1,40 @@ +class Bundler::ConnectionPool + if Process.respond_to?(:fork) + INSTANCES = ObjectSpace::WeakMap.new + private_constant :INSTANCES + + def self.after_fork + INSTANCES.each_value do |pool| + # We're in after_fork, so we know all other threads are dead. + # All we need to do is ensure the main thread doesn't have a + # checked out connection + pool.checkin(force: true) + pool.reload do |connection| + # Unfortunately we don't know what method to call to close the connection, + # so we try the most common one. + connection.close if connection.respond_to?(:close) + end + end + nil + end + + module ForkTracker + def _fork + pid = super + if pid == 0 + Bundler::ConnectionPool.after_fork + end + pid + end + end + Process.singleton_class.prepend(ForkTracker) + else + # JRuby, et al + INSTANCES = nil + private_constant :INSTANCES + + def self.after_fork + # noop + end + end +end diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb index 026d2c5be27a4b..d62a44159b5db7 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb @@ -5,7 +5,7 @@ # # Examples: # -# ts = TimedStack.new(1) { MyConnection.new } +# ts = TimedStack.new(size: 1) { MyConnection.new } # # # fetch a connection # conn = ts.pop @@ -22,7 +22,7 @@ class Bundler::ConnectionPool::TimedStack ## # Creates a new pool with +size+ connections that are created from the given # +block+. - def initialize(size = 0, &block) + def initialize(size: 0, &block) @create_block = block @created = 0 @que = [] @@ -33,15 +33,15 @@ def initialize(size = 0, &block) end ## - # Returns +obj+ to the stack. +options+ is ignored in TimedStack but may be + # Returns +obj+ to the stack. Additional kwargs are ignored in TimedStack but may be # used by subclasses that extend TimedStack. - def push(obj, options = {}) + def push(obj, **) @mutex.synchronize do if @shutdown_block @created -= 1 unless @created == 0 @shutdown_block.call(obj) else - store_connection obj, options + store_connection obj, ** end @resource.broadcast @@ -58,28 +58,23 @@ def push(obj, options = {}) # @option options [Class] :exception (Bundler::ConnectionPool::TimeoutError) Exception class to raise # if an entry was not available within the timeout period. Use `exception: false` to return nil. # - # The +timeout+ argument will be removed in 3.0. # Other options may be used by subclasses that extend TimedStack. - def pop(timeout = 0.5, options = {}) - options, timeout = timeout, 0.5 if Hash === timeout - timeout = options.fetch :timeout, timeout - + def pop(timeout: 0.5, exception: Bundler::ConnectionPool::TimeoutError, **) deadline = current_time + timeout @mutex.synchronize do loop do raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block - if (conn = try_fetch_connection(options)) + if (conn = try_fetch_connection(**)) return conn end - connection = try_create(options) + connection = try_create(**) return connection if connection to_wait = deadline - current_time if to_wait <= 0 - exc = options.fetch(:exception, Bundler::ConnectionPool::TimeoutError) - if exc - raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" + if exception + raise exception, "Waited #{timeout} sec, #{length}/#{@max} available" else return nil end @@ -108,21 +103,20 @@ def shutdown(reload: false, &block) ## # Reaps connections that were checked in more than +idle_seconds+ ago. - def reap(idle_seconds, &block) - raise ArgumentError, "reap must receive a block" unless block + def reap(idle_seconds:) + raise ArgumentError, "reap must receive a block" unless block_given? raise ArgumentError, "idle_seconds must be a number" unless idle_seconds.is_a?(Numeric) raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block - idle.times do - conn = - @mutex.synchronize do - raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block - - reserve_idle_connection(idle_seconds) - end + count = idle + count.times do + conn = @mutex.synchronize do + raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block + reserve_idle_connection(idle_seconds) + end break unless conn - block.call(conn) + yield conn end end @@ -162,15 +156,15 @@ def current_time # This method must returns a connection from the stack if one exists. Allows # subclasses with expensive match/search algorithms to avoid double-handling # their stack. - def try_fetch_connection(options = nil) - connection_stored?(options) && fetch_connection(options) + def try_fetch_connection(**) + connection_stored?(**) && fetch_connection(**) end ## # This is an extension point for TimedStack and is called with a mutex. # # This method must returns true if a connection is available on the stack. - def connection_stored?(options = nil) + def connection_stored?(**) !@que.empty? end @@ -178,7 +172,7 @@ def connection_stored?(options = nil) # This is an extension point for TimedStack and is called with a mutex. # # This method must return a connection from the stack. - def fetch_connection(options = nil) + def fetch_connection(**) @que.pop&.first end @@ -186,8 +180,8 @@ def fetch_connection(options = nil) # This is an extension point for TimedStack and is called with a mutex. # # This method must shut down all connections on the stack. - def shutdown_connections(options = nil) - while (conn = try_fetch_connection(options)) + def shutdown_connections(**) + while (conn = try_fetch_connection(**)) @created -= 1 unless @created == 0 @shutdown_block.call(conn) end @@ -203,6 +197,8 @@ def reserve_idle_connection(idle_seconds) @created -= 1 unless @created == 0 + # Most active elements are at the tail of the array. + # Most idle will be at the head so `shift` rather than `pop`. @que.shift.first end @@ -211,14 +207,17 @@ def reserve_idle_connection(idle_seconds) # # Returns true if the first connection in the stack has been idle for more than idle_seconds def idle_connections?(idle_seconds) - connection_stored? && (current_time - @que.first.last > idle_seconds) + return unless connection_stored? + # Most idle will be at the head so `first` + age = (current_time - @que.first.last) + age > idle_seconds end ## # This is an extension point for TimedStack and is called with a mutex. # # This method must return +obj+ to the stack. - def store_connection(obj, options = nil) + def store_connection(obj, **) @que.push [obj, current_time] end @@ -227,7 +226,7 @@ def store_connection(obj, options = nil) # # This method must create a connection if and only if the total number of # connections allowed has not been met. - def try_create(options = nil) + def try_create(**) unless @created == @max object = @create_block.call @created += 1 diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb index 2e9eebdbb6d2dc..509d5af2db81fd 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb @@ -1,3 +1,3 @@ class Bundler::ConnectionPool - VERSION = "2.5.5" + VERSION = "3.0.2" end diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb index dd796d1021f9d1..d11d6c8eb4b129 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb @@ -2,20 +2,20 @@ class Bundler::ConnectionPool class Wrapper < ::BasicObject METHODS = [:with, :pool_shutdown, :wrapped_pool] - def initialize(options = {}, &block) - @pool = options.fetch(:pool) { ::Bundler::ConnectionPool.new(options, &block) } + def initialize(**options, &) + @pool = options.fetch(:pool) { ::Bundler::ConnectionPool.new(**options, &) } end def wrapped_pool @pool end - def with(&block) - @pool.with(&block) + def with(**, &) + @pool.with(**, &) end - def pool_shutdown(&block) - @pool.shutdown(&block) + def pool_shutdown(&) + @pool.shutdown(&) end def pool_size @@ -26,31 +26,18 @@ def pool_available @pool.available end - def respond_to?(id, *args) - METHODS.include?(id) || with { |c| c.respond_to?(id, *args) } + def respond_to?(id, *, **) + METHODS.include?(id) || with { |c| c.respond_to?(id, *, **) } end - # rubocop:disable Style/MissingRespondToMissing - if ::RUBY_VERSION >= "3.0.0" - def method_missing(name, *args, **kwargs, &block) - with do |connection| - connection.send(name, *args, **kwargs, &block) - end - end - elsif ::RUBY_VERSION >= "2.7.0" - ruby2_keywords def method_missing(name, *args, &block) - with do |connection| - connection.send(name, *args, &block) - end - end - else - def method_missing(name, *args, &block) - with do |connection| - connection.send(name, *args, &block) - end + def respond_to_missing?(id, *, **) + with { |c| c.respond_to?(id, *, **) } + end + + def method_missing(name, *, **, &) + with do |connection| + connection.send(name, *, **, &) end end - # rubocop:enable Style/MethodMissingSuper - # rubocop:enable Style/MissingRespondToMissing end end diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb index 93e403a5bb367d..00f2bca14ad31f 100644 --- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb @@ -1,10 +1,7 @@ require_relative '../../../../../vendored_net_http' require_relative '../../../../../vendored_uri' -begin - require 'cgi/escape' -rescue LoadError - require 'cgi/util' # for escaping -end +require 'cgi/escape' +require 'cgi/util' unless defined?(CGI::EscapeExt) require_relative '../../../../connection_pool/lib/connection_pool' autoload :OpenSSL, 'openssl' @@ -180,7 +177,7 @@ class Gem::Net::HTTP::Persistent ## # The version of Gem::Net::HTTP::Persistent you are using - VERSION = '4.0.6' + VERSION = '4.0.8' ## # Error class for errors raised by Gem::Net::HTTP::Persistent. Various @@ -475,6 +472,13 @@ def self.detect_idle_timeout uri, max = 10 attr_reader :verify_hostname + + ## + # Sets whether to ignore end-of-file when reading a response body + # with Content-Length headers. + + attr_accessor :ignore_eof + ## # Creates a new Gem::Net::HTTP::Persistent. # @@ -514,6 +518,7 @@ def initialize name: nil, proxy: nil, pool_size: DEFAULT_POOL_SIZE @max_retries = 1 @socket_options = [] @ssl_generation = 0 # incremented when SSL session variables change + @ignore_eof = nil @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if Socket.const_defined? :TCP_NODELAY @@ -642,6 +647,7 @@ def connection_for uri reset connection end + http.ignore_eof = @ignore_eof if @ignore_eof http.keep_alive_timeout = @idle_timeout if @idle_timeout http.max_retries = @max_retries if http.respond_to?(:max_retries=) http.read_timeout = @read_timeout if @read_timeout diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb index 04a1e754bfdd0d..5e45ea77d1afde 100644 --- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb @@ -4,7 +4,7 @@ class Gem::Net::HTTP::Persistent::Pool < Bundler::ConnectionPool # :nodoc: attr_reader :key # :nodoc: def initialize(options = {}, &block) - super + super(**options, &block) @available = Gem::Net::HTTP::Persistent::TimedStackMulti.new(@size, &block) @key = "current-#{@available.object_id}" diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb index 034fbe39b811f8..57dfefdaae5098 100644 --- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +++ b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb @@ -1,5 +1,10 @@ class Gem::Net::HTTP::Persistent::TimedStackMulti < Bundler::ConnectionPool::TimedStack # :nodoc: + ## + # Detects if Bundler::ConnectionPool 3.0+ is being used (needed for TimedStack subclass compatibility) + + CP_USES_KEYWORD_ARGS = Gem::Version.new(Bundler::ConnectionPool::VERSION) >= Gem::Version.new('3.0.0') # :nodoc: + ## # Returns a new hash that has arrays for keys # @@ -11,7 +16,11 @@ def self.hash_of_arrays # :nodoc: end def initialize(size = 0, &block) - super + if CP_USES_KEYWORD_ARGS + super(size: size, &block) + else + super(size, &block) + end @enqueued = 0 @ques = self.class.hash_of_arrays diff --git a/lib/bundler/vendor/thor/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb index 945bdbd5515fe5..b78593a6ac3b7a 100644 --- a/lib/bundler/vendor/thor/lib/thor.rb +++ b/lib/bundler/vendor/thor/lib/thor.rb @@ -625,7 +625,7 @@ def normalize_command_name(meth) #:nodoc: # alias name. def find_command_possibilities(meth) len = meth.to_s.length - possibilities = all_commands.reject { |_k, c| c.hidden? }.merge(map).keys.select { |n| meth == n[0, len] }.sort + possibilities = all_commands.reject {|k, v| v.is_a?(HiddenCommand) }.merge(map).keys.select { |n| meth == n[0, len] }.sort unique_possibilities = possibilities.map { |k| map[k] || k }.uniq if possibilities.include?(meth) @@ -671,4 +671,37 @@ def help(command = nil, subcommand = false) self.class.help(shell, subcommand) end end + + map TREE_MAPPINGS => :tree + + desc "tree", "Print a tree of all available commands" + def tree + build_command_tree(self.class, "") + end + +private + + def build_command_tree(klass, indent) + # Print current class name if it's not the root Bundler::Thor class + unless klass == Bundler::Thor + say "#{indent}#{klass.namespace || 'default'}", :blue + indent = "#{indent} " + end + + # Print all commands for this class + visible_commands = klass.commands.reject { |_, cmd| cmd.hidden? || cmd.name == "help" } + commands_count = visible_commands.count + visible_commands.sort.each_with_index do |(command_name, command), i| + description = command.description.split("\n").first || "" + icon = i == (commands_count - 1) ? "└─" : "├─" + say "#{indent}#{icon} ", nil, false + say command_name, :green, false + say " (#{description})" unless description.empty? + end + + # Print all subcommands (from registered Bundler::Thor subclasses) + klass.subcommand_classes.each do |_, subclass| + build_command_tree(subclass, indent) + end + end end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb index 70526e615f1859..229f294b10c1ca 100644 --- a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb @@ -2,6 +2,38 @@ class Bundler::Thor module Actions + WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"} + + # Injects the given content into a file, raising an error if the contents of + # the file are not changed. Different from gsub_file, this method is reversible. + # + # ==== Parameters + # destination:: Relative path to the destination root + # data:: Data to add to the file. Can be given as a block. + # config:: give :verbose => false to not log the status and the flag + # for injection (:after or :before) or :force => true for + # insert two or more times the same content. + # + # ==== Examples + # + # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" + # + # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do + # gems = ask "Which gems would you like to add?" + # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") + # end + # + def insert_into_file!(destination, *args, &block) + data = block_given? ? block : args.shift + + config = args.shift || {} + config[:after] = /\z/ unless config.key?(:before) || config.key?(:after) + config = config.merge({error_on_no_change: true}) + + action InjectIntoFile.new(self, destination, data, config) + end + alias_method :inject_into_file!, :insert_into_file! + # Injects the given content into a file. Different from gsub_file, this # method is reversible. # @@ -21,8 +53,6 @@ module Actions # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") # end # - WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"} - def insert_into_file(destination, *args, &block) data = block_given? ? block : args.shift @@ -47,6 +77,7 @@ def initialize(base, destination, data, config) @replacement = data.is_a?(Proc) ? data.call : data @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) + @error_on_no_change = @config.fetch(:error_on_no_change, false) end def invoke! @@ -59,6 +90,8 @@ def invoke! if exists? if replace!(/#{flag}/, content, config[:force]) say_status(:invoke) + elsif @error_on_no_change + raise Bundler::Thor::Error, "The content of #{destination} did not change" elsif replacement_present? say_status(:unchanged, color: :blue) else diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb index b156899c1e0a4d..f378a48d589e36 100644 --- a/lib/bundler/vendor/thor/lib/thor/base.rb +++ b/lib/bundler/vendor/thor/lib/thor/base.rb @@ -13,8 +13,9 @@ class Bundler::Thor autoload :RakeCompat, File.expand_path("rake_compat", __dir__) autoload :Group, File.expand_path("group", __dir__) - # Shortcuts for help. + # Shortcuts for help and tree commands. HELP_MAPPINGS = %w(-h -? --help -D) + TREE_MAPPINGS = %w(-t --tree) # Bundler::Thor methods that should not be overwritten by the user. THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb index da02b9422758e5..fcd84f45e76220 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb @@ -311,13 +311,11 @@ def file_collision_help(block_given) #:nodoc: end def show_diff(destination, content) #:nodoc: - diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u" - require "tempfile" Tempfile.open(File.basename(destination), File.dirname(destination), binmode: true) do |temp| temp.write content temp.rewind - system %(#{diff_cmd} "#{destination}" "#{temp.path}") + system(*diff_tool, destination, temp.path) end end @@ -369,15 +367,25 @@ def answer_match(possibilities, answer, case_insensitive) def merge(destination, content) #:nodoc: require "tempfile" - Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp| + Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination), binmode: true) do |temp| temp.write content temp.rewind - system(merge_tool, temp.path, destination) + system(*merge_tool, temp.path, destination) end end def merge_tool #:nodoc: - @merge_tool ||= ENV["THOR_MERGE"] || "git difftool --no-index" + @merge_tool ||= begin + require "shellwords" + Shellwords.split(ENV["THOR_MERGE"] || "git difftool --no-index") + end + end + + def diff_tool #:nodoc: + @diff_cmd ||= begin + require "shellwords" + Shellwords.split(ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u") + end end end end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/lib/bundler/vendor/thor/lib/thor/shell/color.rb index 5d708fadcad2dd..b09675682c871a 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell/color.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/color.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "basic" class Bundler::Thor diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb index 5474a2f71badc8..3fe3e3ae82e1cc 100644 --- a/lib/bundler/vendor/thor/lib/thor/version.rb +++ b/lib/bundler/vendor/thor/lib/thor/version.rb @@ -1,3 +1,3 @@ class Bundler::Thor - VERSION = "1.4.0" + VERSION = "1.5.0" end diff --git a/lib/rubygems/vendor/optparse/lib/optparse.rb b/lib/rubygems/vendor/optparse/lib/optparse.rb index d39d9dd4e02f9d..66d5fe390e8ffc 100644 --- a/lib/rubygems/vendor/optparse/lib/optparse.rb +++ b/lib/rubygems/vendor/optparse/lib/optparse.rb @@ -426,8 +426,9 @@ # class Gem::OptionParser # The version string - VERSION = "0.8.0" - Version = VERSION # for compatibility + VERSION = "0.8.1" + # An alias for compatibility + Version = VERSION # :stopdoc: NoArgument = [NO_ARGUMENT = :NONE, nil].freeze @@ -471,7 +472,6 @@ def candidate(key, icase = false, pat = nil, &_) Completion.candidate(key, icase, pat, &method(:each)) end - public def complete(key, icase = false, pat = nil) candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size} if candidates.size == 1 @@ -561,7 +561,7 @@ def initialize(pattern = nil, conv = nil, # Parses +arg+ and returns rest of +arg+ and matched portion to the # argument pattern. Yields when the pattern doesn't match substring. # - def parse_arg(arg) # :nodoc: + private def parse_arg(arg) # :nodoc: pattern or return nil, [arg] unless m = pattern.match(arg) yield(InvalidArgument, arg) @@ -579,14 +579,13 @@ def parse_arg(arg) # :nodoc: yield(InvalidArgument, arg) # didn't match whole arg return arg[s.length..-1], m end - private :parse_arg # # Parses argument, converts and returns +arg+, +block+ and result of # conversion. Yields at semi-error condition instead of raising an # exception. # - def conv_arg(arg, val = []) # :nodoc: + private def conv_arg(arg, val = []) # :nodoc: v, = *val if conv val = conv.call(*val) @@ -598,7 +597,6 @@ def conv_arg(arg, val = []) # :nodoc: end return arg, block, val end - private :conv_arg # # Produces the summary text. Each line of the summary is yielded to the @@ -882,14 +880,13 @@ def reject(t) # +lopts+:: Long style option list. # +nlopts+:: Negated long style options list. # - def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc: + private def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc: sopts.each {|o| @short[o] = sw} if sopts lopts.each {|o| @long[o] = sw} if lopts nlopts.each {|o| @long[o] = nsw} if nsw and nlopts used = @short.invert.update(@long.invert) @list.delete_if {|o| Switch === o and !used[o]} end - private :update # # Inserts +switch+ at the head of the list, and associates short, long @@ -1458,14 +1455,13 @@ def to_a; summarize("#{banner}".split(/^/)) end # +prv+:: Previously specified argument. # +msg+:: Exception message. # - def notwice(obj, prv, msg) # :nodoc: + private def notwice(obj, prv, msg) # :nodoc: unless !prv or prv == obj raise(ArgumentError, "argument #{msg} given twice: #{obj}", ParseError.filter_backtrace(caller(2))) end obj end - private :notwice SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc: @@ -1732,7 +1728,7 @@ def order!(argv = default_argv, into: nil, **keywords, &nonopt) parse_in_order(argv, setter, **keywords, &nonopt) end - def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, &nonopt) # :nodoc: + private def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, &nonopt) # :nodoc: opt, arg, val, rest = nil nonopt ||= proc {|a| throw :terminate, a} argv.unshift(arg) if arg = catch(:terminate) { @@ -1823,10 +1819,9 @@ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, argv end - private :parse_in_order # Calls callback with _val_. - def callback!(cb, max_arity, *args) # :nodoc: + private def callback!(cb, max_arity, *args) # :nodoc: args.compact! if (size = args.size) < max_arity and cb.to_proc.lambda? @@ -1836,7 +1831,6 @@ def callback!(cb, max_arity, *args) # :nodoc: end cb.call(*args) end - private :callback! # # Parses command line arguments +argv+ in permutation mode and returns @@ -1950,24 +1944,22 @@ def self.getopts(*args, symbolize_names: false) # Traverses @stack, sending each element method +id+ with +args+ and # +block+. # - def visit(id, *args, &block) # :nodoc: + private def visit(id, *args, &block) # :nodoc: @stack.reverse_each do |el| el.__send__(id, *args, &block) end nil end - private :visit # # Searches +key+ in @stack for +id+ hash and returns or yields the result. # - def search(id, key) # :nodoc: + private def search(id, key) # :nodoc: block_given = block_given? visit(:search, id, key) do |k| return block_given ? yield(k) : k end end - private :search # # Completes shortened long style option switch and returns pair of @@ -1978,7 +1970,7 @@ def search(id, key) # :nodoc: # +icase+:: Search case insensitive if true. # +pat+:: Optional pattern for completion. # - def complete(typ, opt, icase = false, *pat) # :nodoc: + private def complete(typ, opt, icase = false, *pat) # :nodoc: if pat.empty? search(typ, opt) {|sw| return [sw, opt]} # exact match or... end @@ -1988,7 +1980,6 @@ def complete(typ, opt, icase = false, *pat) # :nodoc: exc = ambiguous ? AmbiguousOption : InvalidOption raise exc.new(opt, additional: proc {|o| additional_message(typ, o)}) end - private :complete # # Returns additional info. @@ -2323,42 +2314,42 @@ def message # Raises when ambiguously completable string is encountered. # class AmbiguousOption < ParseError - const_set(:Reason, 'ambiguous option') + Reason = 'ambiguous option' # :nodoc: end # # Raises when there is an argument for a switch which takes no argument. # class NeedlessArgument < ParseError - const_set(:Reason, 'needless argument') + Reason = 'needless argument' # :nodoc: end # # Raises when a switch with mandatory argument has no argument. # class MissingArgument < ParseError - const_set(:Reason, 'missing argument') + Reason = 'missing argument' # :nodoc: end # # Raises when switch is undefined. # class InvalidOption < ParseError - const_set(:Reason, 'invalid option') + Reason = 'invalid option' # :nodoc: end # # Raises when the given argument does not match required format. # class InvalidArgument < ParseError - const_set(:Reason, 'invalid argument') + Reason = 'invalid argument' # :nodoc: end # # Raises when the given argument word can't be completed uniquely. # class AmbiguousArgument < InvalidArgument - const_set(:Reason, 'ambiguous argument') + Reason = 'ambiguous argument' # :nodoc: end # @@ -2457,9 +2448,11 @@ def initialize(*args) # :nodoc: # and DecimalNumeric. See Acceptable argument classes (in source code). # module Acceptables - const_set(:DecimalInteger, Gem::OptionParser::DecimalInteger) - const_set(:OctalInteger, Gem::OptionParser::OctalInteger) - const_set(:DecimalNumeric, Gem::OptionParser::DecimalNumeric) + # :stopdoc: + DecimalInteger = Gem::OptionParser::DecimalInteger + OctalInteger = Gem::OptionParser::OctalInteger + DecimalNumeric = Gem::OptionParser::DecimalNumeric + # :startdoc: end end diff --git a/lib/rubygems/vendor/resolv/lib/resolv.rb b/lib/rubygems/vendor/resolv/lib/resolv.rb index 4f48e0642bf48a..f922c731b05026 100644 --- a/lib/rubygems/vendor/resolv/lib/resolv.rb +++ b/lib/rubygems/vendor/resolv/lib/resolv.rb @@ -35,7 +35,7 @@ class Gem::Resolv # The version string - VERSION = "0.7.0" + VERSION = "0.7.1" ## # Looks up the first IP address for +name+. @@ -487,13 +487,18 @@ def each_name(address) # * Gem::Resolv::DNS::Resource::IN::A # * Gem::Resolv::DNS::Resource::IN::AAAA # * Gem::Resolv::DNS::Resource::IN::ANY + # * Gem::Resolv::DNS::Resource::IN::CAA # * Gem::Resolv::DNS::Resource::IN::CNAME # * Gem::Resolv::DNS::Resource::IN::HINFO + # * Gem::Resolv::DNS::Resource::IN::HTTPS + # * Gem::Resolv::DNS::Resource::IN::LOC # * Gem::Resolv::DNS::Resource::IN::MINFO # * Gem::Resolv::DNS::Resource::IN::MX # * Gem::Resolv::DNS::Resource::IN::NS # * Gem::Resolv::DNS::Resource::IN::PTR # * Gem::Resolv::DNS::Resource::IN::SOA + # * Gem::Resolv::DNS::Resource::IN::SRV + # * Gem::Resolv::DNS::Resource::IN::SVCB # * Gem::Resolv::DNS::Resource::IN::TXT # * Gem::Resolv::DNS::Resource::IN::WKS # @@ -721,7 +726,8 @@ def request(sender, tout) begin reply, from = recv_reply(select_result[0]) rescue Errno::ECONNREFUSED, # GNU/Linux, FreeBSD - Errno::ECONNRESET # Windows + Errno::ECONNRESET, # Windows + EOFError # No name server running on the server? # Don't wait anymore. raise ResolvTimeout @@ -930,8 +936,11 @@ def initialize(host, port=Port) end def recv_reply(readable_socks) - len = readable_socks[0].read(2).unpack('n')[0] + len_data = readable_socks[0].read(2) + raise EOFError if len_data.nil? || len_data.bytesize != 2 + len = len_data.unpack('n')[0] reply = @socks[0].read(len) + raise EOFError if reply.nil? || reply.bytesize != len return reply, nil end diff --git a/lib/rubygems/vendor/timeout/lib/timeout.rb b/lib/rubygems/vendor/timeout/lib/timeout.rb index 376b8c0e2b9e99..5235bc98460b14 100644 --- a/lib/rubygems/vendor/timeout/lib/timeout.rb +++ b/lib/rubygems/vendor/timeout/lib/timeout.rb @@ -20,9 +20,9 @@ module Gem::Timeout # The version - VERSION = "0.4.4" + VERSION = "0.6.1" - # Internal error raised to when a timeout is triggered. + # Internal exception raised to when a timeout is triggered. class ExitException < Exception def exception(*) # :nodoc: self @@ -44,12 +44,101 @@ def self.handle_timeout(message) # :nodoc: end # :stopdoc: - CONDVAR = ConditionVariable.new - QUEUE = Queue.new - QUEUE_MUTEX = Mutex.new - TIMEOUT_THREAD_MUTEX = Mutex.new - @timeout_thread = nil - private_constant :CONDVAR, :QUEUE, :QUEUE_MUTEX, :TIMEOUT_THREAD_MUTEX + + # We keep a private reference so that time mocking libraries won't break Gem::Timeout. + GET_TIME = Process.method(:clock_gettime) + if defined?(Ractor.make_shareable) + # Ractor.make_shareable(Method) only works on Ruby 4+ + Ractor.make_shareable(GET_TIME) rescue nil + end + private_constant :GET_TIME + + class State + def initialize + @condvar = ConditionVariable.new + @queue = Queue.new + @queue_mutex = Mutex.new + + @timeout_thread = nil + @timeout_thread_mutex = Mutex.new + end + + if defined?(Ractor.store_if_absent) && defined?(Ractor.shareable?) && Ractor.shareable?(GET_TIME) + # Ractor support if + # 1. Ractor.store_if_absent is available + # 2. Method object can be shareable (4.0~) + def self.instance + Ractor.store_if_absent :timeout_gem_state do + State.new + end + end + else + GLOBAL_STATE = State.new + + def self.instance + GLOBAL_STATE + end + end + + def create_timeout_thread + # Threads unexpectedly inherit the interrupt mask: https://github.com/ruby/timeout/issues/41 + # So reset the interrupt mask to the default one for the timeout thread + Thread.handle_interrupt(Object => :immediate) do + watcher = Thread.new do + requests = [] + while true + until @queue.empty? and !requests.empty? # wait to have at least one request + req = @queue.pop + requests << req unless req.done? + end + closest_deadline = requests.min_by(&:deadline).deadline + + now = 0.0 + @queue_mutex.synchronize do + while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) < closest_deadline and @queue.empty? + @condvar.wait(@queue_mutex, closest_deadline - now) + end + end + + requests.each do |req| + req.interrupt if req.expired?(now) + end + requests.reject!(&:done?) + end + end + + if !watcher.group.enclosed? && (!defined?(Ractor.main?) || Ractor.main?) + ThreadGroup::Default.add(watcher) + end + + watcher.name = "Gem::Timeout stdlib thread" + watcher.thread_variable_set(:"\0__detached_thread__", true) + watcher + end + end + + def ensure_timeout_thread_created + unless @timeout_thread&.alive? + # If the Mutex is already owned we are in a signal handler. + # In that case, just return and let the main thread create the Gem::Timeout thread. + return if @timeout_thread_mutex.owned? + + Sync.synchronize @timeout_thread_mutex do + unless @timeout_thread&.alive? + @timeout_thread = create_timeout_thread + end + end + end + end + + def add_request(request) + Sync.synchronize @queue_mutex do + @queue << request + @condvar.signal + end + end + end + private_constant :State class Request attr_reader :deadline @@ -64,6 +153,7 @@ def initialize(thread, timeout, exception_class, message) @done = false # protected by @mutex end + # Only called by the timeout thread, so does not need Sync.synchronize def done? @mutex.synchronize do @done @@ -74,6 +164,7 @@ def expired?(now) now >= @deadline end + # Only called by the timeout thread, so does not need Sync.synchronize def interrupt @mutex.synchronize do unless @done @@ -84,64 +175,36 @@ def interrupt end def finished - @mutex.synchronize do + Sync.synchronize @mutex do @done = true end end end private_constant :Request - def self.create_timeout_thread - watcher = Thread.new do - requests = [] - while true - until QUEUE.empty? and !requests.empty? # wait to have at least one request - req = QUEUE.pop - requests << req unless req.done? - end - closest_deadline = requests.min_by(&:deadline).deadline - - now = 0.0 - QUEUE_MUTEX.synchronize do - while (now = GET_TIME.call(Process::CLOCK_MONOTONIC)) < closest_deadline and QUEUE.empty? - CONDVAR.wait(QUEUE_MUTEX, closest_deadline - now) - end - end - - requests.each do |req| - req.interrupt if req.expired?(now) - end - requests.reject!(&:done?) - end - end - ThreadGroup::Default.add(watcher) unless watcher.group.enclosed? - watcher.name = "Gem::Timeout stdlib thread" - watcher.thread_variable_set(:"\0__detached_thread__", true) - watcher - end - private_class_method :create_timeout_thread - - def self.ensure_timeout_thread_created - unless @timeout_thread and @timeout_thread.alive? - # If the Mutex is already owned we are in a signal handler. - # In that case, just return and let the main thread create the @timeout_thread. - return if TIMEOUT_THREAD_MUTEX.owned? - TIMEOUT_THREAD_MUTEX.synchronize do - unless @timeout_thread and @timeout_thread.alive? - @timeout_thread = create_timeout_thread - end + module Sync + # Calls mutex.synchronize(&block) but if that fails on CRuby due to being in a trap handler, + # run mutex.synchronize(&block) in a separate Thread instead. + def self.synchronize(mutex, &block) + begin + mutex.synchronize(&block) + rescue ThreadError => e + raise e unless e.message == "can't be called from trap context" + # Workaround CRuby issue https://bugs.ruby-lang.org/issues/19473 + # which raises on Mutex#synchronize in trap handler. + # It's expensive to create a Thread just for this, + # but better than failing. + Thread.new { + mutex.synchronize(&block) + }.join end end end - - # We keep a private reference so that time mocking libraries won't break - # Gem::Timeout. - GET_TIME = Process.method(:clock_gettime) - private_constant :GET_TIME + private_constant :Sync # :startdoc: - # Perform an operation in a block, raising an error if it takes longer than + # Perform an operation in a block, raising an exception if it takes longer than # +sec+ seconds to complete. # # +sec+:: Number of seconds to wait for the block to terminate. Any non-negative number @@ -149,45 +212,91 @@ def self.ensure_timeout_thread_created # value of 0 or +nil+ will execute the block without any timeout. # Any negative number will raise an ArgumentError. # +klass+:: Exception Class to raise if the block fails to terminate - # in +sec+ seconds. Omitting will use the default, Gem::Timeout::Error + # in +sec+ seconds. Omitting will use the default, Gem::Timeout::Error. # +message+:: Error message to raise with Exception Class. - # Omitting will use the default, "execution expired" + # Omitting will use the default, "execution expired". # # Returns the result of the block *if* the block completed before - # +sec+ seconds, otherwise throws an exception, based on the value of +klass+. + # +sec+ seconds, otherwise raises an exception, based on the value of +klass+. + # + # The exception raised to terminate the given block is the given +klass+, or + # Gem::Timeout::ExitException if +klass+ is not given. The reason for that behavior + # is that Gem::Timeout::Error inherits from RuntimeError and might be caught unexpectedly by +rescue+. + # Gem::Timeout::ExitException inherits from Exception so it will only be rescued by rescue Exception. + # Note that the Gem::Timeout::ExitException is translated to a Gem::Timeout::Error once it reaches the Gem::Timeout.timeout call, + # so outside that call it will be a Gem::Timeout::Error. # - # The exception thrown to terminate the given block cannot be rescued inside - # the block unless +klass+ is given explicitly. However, the block can use - # ensure to prevent the handling of the exception. For that reason, this - # method cannot be relied on to enforce timeouts for untrusted blocks. + # In general, be aware that the code block may rescue the exception, and in such a case not respect the timeout. + # Also, the block can use +ensure+ to prevent the handling of the exception. + # For those reasons, this method cannot be relied on to enforce timeouts for untrusted blocks. # # If a scheduler is defined, it will be used to handle the timeout by invoking - # Scheduler#timeout_after. + # Fiber::Scheduler#timeout_after. # # Note that this is both a method of module Gem::Timeout, so you can include # Gem::Timeout into your classes so they have a #timeout method, as well as # a module method, so you can call it directly as Gem::Timeout.timeout(). - def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ + # + # ==== Ensuring the exception does not fire inside ensure blocks + # + # When using Gem::Timeout.timeout, it can be desirable to ensure the timeout exception does not fire inside an +ensure+ block. + # The simplest and best way to do so is to put the Gem::Timeout.timeout call inside the body of the +begin+/+ensure+/+end+: + # + # begin + # Gem::Timeout.timeout(sec) { some_long_operation } + # ensure + # cleanup # safe, cannot be interrupted by timeout + # end + # + # If that is not feasible, e.g. if there are +ensure+ blocks inside +some_long_operation+, + # they need to not be interrupted by timeout, and it's not possible to move these ensure blocks outside, + # one can use Thread.handle_interrupt to delay the timeout exception like so: + # + # Thread.handle_interrupt(Gem::Timeout::Error => :never) { + # Gem::Timeout.timeout(sec, Gem::Timeout::Error) do + # setup # timeout cannot happen here, no matter how long it takes + # Thread.handle_interrupt(Gem::Timeout::Error => :immediate) { + # some_long_operation # timeout can happen here + # } + # ensure + # cleanup # timeout cannot happen here, no matter how long it takes + # end + # } + # + # An important thing to note is the need to pass an exception +klass+ to Gem::Timeout.timeout, + # otherwise it does not work. Specifically, using Thread.handle_interrupt(Gem::Timeout::ExitException => ...) + # is unsupported and causes subtle errors like raising the wrong exception outside the block, do not use that. + # + # Note that Thread.handle_interrupt is somewhat dangerous because if setup or cleanup hangs + # then the current thread will hang too and the timeout will never fire. + # Also note the block might run for longer than +sec+ seconds: + # e.g. +some_long_operation+ executes for +sec+ seconds + whatever time cleanup takes. + # + # If you want the timeout to only happen on blocking operations, one can use +:on_blocking+ + # instead of +:immediate+. However, that means if the block uses no blocking operations after +sec+ seconds, + # the block will not be interrupted. + def self.timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ return yield(sec) if sec == nil or sec.zero? raise ArgumentError, "Timeout sec must be a non-negative number" if 0 > sec message ||= "execution expired" if Fiber.respond_to?(:current_scheduler) && (scheduler = Fiber.current_scheduler)&.respond_to?(:timeout_after) - return scheduler.timeout_after(sec, klass || Error, message, &block) - end - - Gem::Timeout.ensure_timeout_thread_created - perform = Proc.new do |exc| - request = Request.new(Thread.current, sec, exc, message) - QUEUE_MUTEX.synchronize do - QUEUE << request - CONDVAR.signal + perform = Proc.new do |exc| + scheduler.timeout_after(sec, exc, message, &block) end - begin - return yield(sec) - ensure - request.finished + else + state = State.instance + state.ensure_timeout_thread_created + + perform = Proc.new do |exc| + request = Request.new(Thread.current, sec, exc, message) + state.add_request(request) + begin + return yield(sec) + ensure + request.finished + end end end @@ -197,5 +306,9 @@ def timeout(sec, klass = nil, message = nil, &block) #:yield: +sec+ Error.handle_timeout(message, &perform) end end - module_function :timeout + + # See Gem::Timeout.timeout + private def timeout(*args, &block) + Gem::Timeout.timeout(*args, &block) + end end diff --git a/prism_compile.c b/prism_compile.c index e9c21a9411ee24..81eda24a254dbb 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -1148,7 +1148,7 @@ pm_compile_conditional(rb_iseq_t *iseq, const pm_node_location_t *node_location, if (then_label->refcnt && else_label->refcnt && PM_BRANCH_COVERAGE_P(iseq)) { conditional_location = pm_code_location(scope_node, node); - branches = decl_branch_base(iseq, PTR2NUM(node), &conditional_location, type == PM_IF_NODE ? "if" : "unless"); + branches = decl_branch_base(iseq, (int) node->node_id, &conditional_location, type == PM_IF_NODE ? "if" : "unless"); } if (then_label->refcnt) { @@ -1278,7 +1278,7 @@ pm_compile_loop(rb_iseq_t *iseq, const pm_node_location_t *node_location, pm_nod // Establish branch coverage for the loop. if (PM_BRANCH_COVERAGE_P(iseq)) { rb_code_location_t loop_location = pm_code_location(scope_node, node); - VALUE branches = decl_branch_base(iseq, PTR2NUM(node), &loop_location, type == PM_WHILE_NODE ? "while" : "until"); + VALUE branches = decl_branch_base(iseq, (int) node->node_id, &loop_location, type == PM_WHILE_NODE ? "while" : "until"); rb_code_location_t branch_location = statements != NULL ? pm_code_location(scope_node, (const pm_node_t *) statements) : loop_location; add_trace_branch_coverage(iseq, ret, &branch_location, branch_location.beg_pos.column, 0, "body", branches); @@ -3789,7 +3789,7 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c .end_pos = { .lineno = end_location.line, .column = end_location.column } }; - branches = decl_branch_base(iseq, PTR2NUM(call_node), &code_location, "&."); + branches = decl_branch_base(iseq, (int) call_node->base.node_id, &code_location, "&."); } PUSH_INSN(ret, location, dup); @@ -7640,7 +7640,7 @@ pm_compile_case_node(rb_iseq_t *iseq, const pm_case_node_t *cast, const pm_node_ if (PM_BRANCH_COVERAGE_P(iseq)) { case_location = pm_code_location(scope_node, (const pm_node_t *) cast); - branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case"); + branches = decl_branch_base(iseq, (int) cast->base.node_id, &case_location, "case"); } // Loop through each clauses in the case node and compile each of @@ -7727,7 +7727,7 @@ pm_compile_case_node(rb_iseq_t *iseq, const pm_case_node_t *cast, const pm_node_ if (PM_BRANCH_COVERAGE_P(iseq)) { case_location = pm_code_location(scope_node, (const pm_node_t *) cast); - branches = decl_branch_base(iseq, PTR2NUM(cast), &case_location, "case"); + branches = decl_branch_base(iseq, (int) cast->base.node_id, &case_location, "case"); } // This is the label where everything will fall into if none of the @@ -7899,7 +7899,7 @@ pm_compile_case_match_node(rb_iseq_t *iseq, const pm_case_match_node_t *node, co if (PM_BRANCH_COVERAGE_P(iseq)) { case_location = pm_code_location(scope_node, (const pm_node_t *) node); - branches = decl_branch_base(iseq, PTR2NUM(node), &case_location, "case"); + branches = decl_branch_base(iseq, (int) node->base.node_id, &case_location, "case"); } // If there is only one pattern, then the behavior changes a bit. It diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb index 8eb3f07be61d54..4a3b9690c09411 100644 --- a/test/coverage/test_coverage.rb +++ b/test/coverage/test_coverage.rb @@ -256,6 +256,34 @@ def test_eval_coverage end; end + def test_branch_coverage_for_eval_repeated + assert_in_out_err(["-W0", *ARGV], <<-"end;", ["2", "2", "[[0, 1], [0, 2]]"], []) + Coverage.start(eval: true, branches: true) + + code = <<-RUBY + def foo(x) + x ? 1 : 2 + end + RUBY + + # Evaluating different code at the same path yields separate entries + eval(code, TOPLEVEL_BINDING, "test.rb", 1) + eval(code.sub("foo", "bar"), TOPLEVEL_BINDING, "test.rb", 10) + foo(true) + r = Coverage.peek_result["test.rb"][:branches] + p r.size + + # Re-evaluating the very same code accumulates the counters instead of + # adding duplicated entries + eval(code, TOPLEVEL_BINDING, "test.rb", 1) + foo(true) + bar(false) + r = Coverage.peek_result["test.rb"][:branches] + p r.size + p r.values.map {|targets| targets.values.sort }.sort + end; + end + def test_eval_negative_lineno assert_in_out_err(ARGV, <<-"end;", ["[1, 1, 1]"], []) Coverage.start(eval: true, lines: true) diff --git a/test/psych/test_parser.rb b/test/psych/test_parser.rb index 786cf016359b17..aeb7424470eb54 100644 --- a/test/psych/test_parser.rb +++ b/test/psych/test_parser.rb @@ -26,6 +26,39 @@ def #{m} *args end end + # Calls Parser#parse again, once, from inside a callback of the parse it is + # already handling. + class ReentrantHandler < Handler + attr_accessor :parser, :inner_yaml + attr_reader :inner_error, :scalars, :empty_calls + + def initialize + @parser = nil + @inner_yaml = nil + @inner_error = nil + @scalars = [] + @empty_calls = 0 + end + + def empty + @empty_calls += 1 + raise "handler#empty keeps being called, the parse loop is not terminating" if @empty_calls > 1000 + end + + def scalar value, anchor, tag, plain, quoted, style + @scalars << value + + inner, @inner_yaml = @inner_yaml, nil + return unless inner + + begin + @parser.parse inner + rescue => e + @inner_error = e + end + end + end + def setup super @handler = EventCatcher.new @@ -70,6 +103,56 @@ def test_exception_memory_leak end end + def test_event_location_exception_is_propagated + klass = Class.new(Psych::Handler) do + def event_location start_line, start_column, end_line, end_column + raise "from event_location" + end + end + + parser = Psych::Parser.new klass.new + 2.times do + ex = assert_raise(RuntimeError) { parser.parse "--- hello\n" } + assert_equal "from event_location", ex.message + end + end + + def test_parse_is_not_reentrant + pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ + + handler = ReentrantHandler.new + handler.inner_yaml = "--- inner\n" + parser = Psych::Parser.new handler + handler.parser = parser + + parser.parse "--- outer\n" + + assert_kind_of Psych::Exception, handler.inner_error + assert_equal ['outer'], handler.scalars + assert_equal 0, handler.empty_calls + + # The in-use flag is cleared when the parse finishes, so the same parser + # can be used again afterwards. + handler.scalars.clear + parser.parse "--- second\n" + assert_equal ['second'], handler.scalars + end + + def test_parse_is_not_reentrant_with_invalid_inner_document + pend "Failing on JRuby" if RUBY_PLATFORM =~ /java/ + + handler = ReentrantHandler.new + handler.inner_yaml = "--- \x00bad\n" + parser = Psych::Parser.new handler + handler.parser = parser + + parser.parse "--- outer\n" + + assert_kind_of Psych::Exception, handler.inner_error + assert_equal ['outer'], handler.scalars + assert_equal 0, handler.empty_calls + end + def test_multiparse 3.times do @parser.parse '--- foo' diff --git a/thread.c b/thread.c index 924b3ece132280..33d45a8c343c67 100644 --- a/thread.c +++ b/thread.c @@ -6443,7 +6443,7 @@ rb_default_coverage(int n) branches = rb_ary_hidden_new_fill(2); /* internal data structures for branch coverage: * - * { branch base node => + * { branch base key (see decl_branch_base) => * [base_type, base_first_lineno, base_first_column, base_last_lineno, base_last_column, { * branch target id => * [target_type, target_first_lineno, target_first_column, target_last_lineno, target_last_column, target_counter_index], @@ -6453,10 +6453,10 @@ rb_default_coverage(int n) * } * * Example: - * { NODE_CASE => + * { [source_hash, node_id, lineno] => * [1, 0, 4, 3, { - * NODE_WHEN => [2, 8, 2, 9, 0], - * NODE_WHEN => [3, 8, 3, 9, 1], + * 0 => [2, 8, 2, 9, 0], + * 1 => [3, 8, 3, 9, 1], * ... * }], * ... diff --git a/vcpkg.json b/vcpkg.json index 29c5a243a632b0..a290c262c71c53 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -7,5 +7,5 @@ "openssl", "zlib" ], - "builtin-baseline": "9e593bb18ea69cc5095e012465dcd675a822ed0d" + "builtin-baseline": "7f3781e19cc7d4e4882a4caec01668c6f7b5c163" } \ No newline at end of file