diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index ce53e33aaea46c..1c4cb533d3f3c7 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -65,10 +65,39 @@ jobs: )}} steps: - - name: Set up Ruby & MSYS2 - uses: ruby/setup-ruby@d8d83c3960843afb664e821fed6be52f37da5267 # v1.231.0 + - uses: msys2/setup-msys2@40677d36a502eb2cf0fb808cc9dec31bf6152638 # v2.28.0 + id: msys2 with: - ruby-version: '3.2' + msystem: UCRT64 + update: true + install: >- + git + make + ruby + autoconf + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-ragel + mingw-w64-ucrt-x86_64-openssl + mingw-w64-ucrt-x86_64-libyaml + mingw-w64-ucrt-x86_64-libffi + + - name: Set up env + id: setup-env + working-directory: + run: | + $msys2 = ${env:MSYS2_LOCATION} + echo $msys2\usr\bin $msys2\ucrt64\bin | + Tee-Object ${env:GITHUB_PATH} -Append -Encoding utf-8 + + # Use the fast device for the temporary directory. + # %TEMP% is inconsistent with %TMP% and test-all expects they are consistent. + # https://github.com/actions/virtual-environments/issues/712#issuecomment-613004302 + $tmp = ${env:RUNNER_TEMP} + echo HOME=$home TMP=$tmp TEMP=$tmp TMPDIR=$tmp | + Tee-Object ${env:GITHUB_ENV} -Append -Encoding utf-8 + shell: pwsh # cmd.exe does not strip spaces before `|`. + env: + MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }} - name: Remove Strawberry Perl pkg-config working-directory: @@ -86,25 +115,38 @@ jobs: - name: Misc system & package info working-directory: run: | - # show where - result=true - for e in gcc.exe ragel.exe make.exe libcrypto-3-x64.dll libssl-3-x64.dll; do - echo ::group::$'\e[93m'$e$'\e[m' - where $e || result=false - echo ::endgroup:: - done - # show version - for e in gcc ragel make "openssl version"; do - case "$e" in *" "*) ;; *) e="$e --version";; esac - echo ::group::$'\e[93m'$e$'\e[m' - $e || result=false - echo ::endgroup:: - done - # show packages - echo ::group::$'\e[93m'Packages$'\e[m' - pacman -Qs mingw-w64-ucrt-x86_64-* | sed -n "s,local/mingw-w64-ucrt-x86_64-,,p" - echo ::endgroup:: - $result + group() { echo ::group::$'\e[94;1m'"$*"$'\e[m'; } + endgroup() { echo ::endgroup::; } + + group Path + cygpath -wa / . $(type -p cygpath bash sh) + endgroup + + I() { + group $1 + run Where type -pa $1 && { [ $# -eq 1 ] || run Version "$@"; } || + failed+=($1) + endgroup + } + run() { local w m=$1; shift; w="$("$@")" && show "$m" && indent "$w"; } + indent() { [ -z "$1" ] || echo "$1" | /bin/sed '/^$/!s/^/ /'; } + show() { echo $'\e[96m'"$*"$'\e[m'; } + + failed=() + + I gcc.exe --version + I ragel.exe --version + I make.exe --version + I openssl.exe version + I libcrypto-3-x64.dll + I libssl-3-x64.dll + + group Packages + pacman -Qs mingw-w64-ucrt-x86_64-* | /bin/sed -n "s,local/mingw-w64-ucrt-x86_64-,,p" + endgroup + + [ ${#failed[@]} -eq 0 ] + shell: sh - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -123,6 +165,7 @@ jobs: run: > ../src/configure --disable-install-doc --prefix=/. --build=$CHOST --host=$CHOST --target=$CHOST + shell: sh - name: make all timeout-minutes: 30 @@ -145,7 +188,6 @@ jobs: - name: test timeout-minutes: 30 run: make test - shell: cmd env: GNUMAKEFLAGS: '' RUBY_TESTOPTS: '-v --tty=no' @@ -153,7 +195,6 @@ jobs: - name: test-all timeout-minutes: 45 - shell: cmd run: | make ${{ StartsWith(matrix.test_task, 'test/') && matrix.test_task || 'test-all' }} env: @@ -168,7 +209,6 @@ jobs: timeout-minutes: 10 run: | make ${{ StartsWith(matrix.test_task, 'spec/') && matrix.test_task || 'test-spec' }} - shell: cmd if: ${{ matrix.test_task == 'check' || matrix.test_task == 'test-spec' || StartsWith(matrix.test_task, 'spec/') }} - uses: ./src/.github/actions/slack @@ -180,4 +220,4 @@ jobs: defaults: run: working-directory: build - shell: sh + shell: cmd diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 053c37ec5d9f23..d7c88393be52e0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -39,7 +39,7 @@ jobs: test_task: test-bundled-gems fail-fast: false - runs-on: windows-${{ matrix.os < 2022 && '2019' || matrix.os }} + runs-on: windows-${{ matrix.os }} if: >- ${{!(false @@ -94,7 +94,7 @@ jobs: - name: Install libraries with vcpkg run: | - vcpkg install --vcpkg-root=C:\Users\runneradmin\scoop\apps\vcpkg\current + vcpkg install --vcpkg-root=%USERPROFILE%\scoop\apps\vcpkg\current working-directory: src - name: Save vcpkg artifact @@ -184,7 +184,7 @@ jobs: - name: Set up Launchable uses: ./.github/actions/launchable/setup with: - os: windows-${{ matrix.os < 2022 && '2019' || matrix.os }} + os: windows-${{ matrix.os }} launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }} builddir: build srcdir: src diff --git a/array.c b/array.c index f485223e34bd21..9f13b1bf5142b3 100644 --- a/array.c +++ b/array.c @@ -3659,9 +3659,9 @@ rb_ary_collect(VALUE ary) /* * call-seq: - * collect! {|element| ... } -> new_array + * collect! {|element| ... } -> self * collect! -> new_enumerator - * map! {|element| ... } -> new_array + * map! {|element| ... } -> self * map! -> new_enumerator * * With a block given, calls the block with each element of +self+ @@ -4755,10 +4755,10 @@ rb_ary_clear(VALUE ary) /* * call-seq: - * fill(object, start = nil, count = nil) -> new_array - * fill(object, range) -> new_array - * fill(start = nil, count = nil) {|element| ... } -> new_array - * fill(range) {|element| ... } -> new_array + * fill(object, start = nil, count = nil) -> self + * fill(object, range) -> self + * fill(start = nil, count = nil) {|element| ... } -> self + * fill(range) {|element| ... } -> self * * Replaces selected elements in +self+; * may add elements to +self+; diff --git a/ast.c b/ast.c index bc2adeacd630a4..04b954385477e1 100644 --- a/ast.c +++ b/ast.c @@ -866,6 +866,12 @@ node_locations(VALUE ast_value, const NODE *node) location_new(&RNODE_IF(node)->if_keyword_loc), location_new(&RNODE_IF(node)->then_keyword_loc), location_new(&RNODE_IF(node)->end_keyword_loc)); + case NODE_IN: + return rb_ary_new_from_args(4, + location_new(nd_code_loc(node)), + location_new(&RNODE_IN(node)->in_keyword_loc), + location_new(&RNODE_IN(node)->then_keyword_loc), + location_new(&RNODE_IN(node)->operator_loc)); case NODE_MODULE: return rb_ary_new_from_args(3, location_new(nd_code_loc(node)), diff --git a/benchmark/string_casecmp.yml b/benchmark/string_casecmp.yml index 2354040a049e3f..88a3555c8a8837 100644 --- a/benchmark/string_casecmp.yml +++ b/benchmark/string_casecmp.yml @@ -20,7 +20,9 @@ benchmark: casecmp-10: lstr10.casecmp(ustr10) casecmp-100: lstr100.casecmp(ustr100) casecmp-1000: lstr1000.casecmp(ustr1000) + casecmp-1000vs10: lstr1000.casecmp(ustr10) casecmp-nonascii1: lnonascii1.casecmp(unonascii1) casecmp-nonascii10: lnonascii10.casecmp(unonascii10) casecmp-nonascii100: lnonascii100.casecmp(unonascii100) casecmp-nonascii1000: lnonascii1000.casecmp(unonascii1000) + casecmp-nonascii1000vs10: lnonascii1000.casecmp(unonascii10) diff --git a/doc/_regexp.rdoc b/doc/_regexp.rdoc index 45a307fad660d3..c9f3742241dd00 100644 --- a/doc/_regexp.rdoc +++ b/doc/_regexp.rdoc @@ -502,7 +502,7 @@ An added _quantifier_ specifies how many matches are required or allowed: /\w*/.match('x') # => # /\w*/.match('xyz') - # => # + # => # - + - Matches one or more times: diff --git a/doc/ruby/options.md b/doc/ruby/options.md index bfbd2530de397e..95f8cf453c9f14 100644 --- a/doc/ruby/options.md +++ b/doc/ruby/options.md @@ -672,6 +672,11 @@ $ ruby --internal-encoding=cesu-8 -e 'puts Encoding::default_internal' CESU-8 ``` +### `--jit` + +Option `--jit` is an alias for option `--yjit`, which enables YJIT; +see additional YJIT options in the [YJIT documentation](rdoc-ref:yjit/yjit.md). + ### `--verbose`: Set `$VERBOSE` Option `--verbose` sets global variable `$VERBOSE` to `true` @@ -681,44 +686,3 @@ and disables input from `$stdin`. Option `--version` prints the version of the Ruby interpreter, then exits. -## Experimental Options - -These options are experimental in the current Ruby release, -and may be modified or withdrawn in later releases. - -### `--jit` - -Option `-jit` enables JIT compilation with the default option. - -#### `--jit-debug` - -Option `--jit-debug` enables JIT debugging (very slow); -adds compiler flags if given. - -#### `--jit-max-cache=num` - -Option `--jit-max-cache=num` sets the maximum number of methods -to be JIT-ed in a cache; default: 100). - -#### `--jit-min-calls=num` - -Option `jit-min-calls=num` sets the minimum number of calls to trigger JIT -(for testing); default: 10000). - -#### `--jit-save-temps` - -Option `--jit-save-temps` saves JIT temporary files in $TMP or /tmp (for testing). - -#### `--jit-verbose` - -Option `--jit-verbose` prints JIT logs of level `num` or less -to `$stderr`; default: 0. - -#### `--jit-wait` - -Option `--jit-wait` waits until JIT compilation finishes every time (for testing). - -#### `--jit-warnings` - -Option `--jit-warnings` enables printing of JIT warnings. - diff --git a/doc/windows.md b/doc/windows.md index 13c797875e6a3d..4ea03d0507d2b7 100644 --- a/doc/windows.md +++ b/doc/windows.md @@ -99,16 +99,18 @@ sh ../../ruby/configure -C --disable-install-doc --with-opt-dir=C:\Users\usernam To cross build arm64 binary: ``` - cmd /k win32\vssetup.cmd -arch arm64 + cmd /k win32\vssetup.cmd -arch=arm64 ``` To cross build x64 binary: ``` - cmd /k win32\vssetup.cmd -arch x64 + cmd /k win32\vssetup.cmd -arch=x64 ``` - See `win32\vssetup.cmd -help` for other command line options. + This batch file is a wrapper of `vsdevcmd.bat` and options are + passed to it as-is. `win32\vssetup.cmd -help` for other command + line options. **Note** building ruby requires following commands. diff --git a/encoding.c b/encoding.c index 7bca8d1b2b3d38..78964476c001d3 100644 --- a/encoding.c +++ b/encoding.c @@ -29,6 +29,7 @@ #include "ruby/util.h" #include "ruby_assert.h" #include "vm_sync.h" +#include "ruby_atomic.h" #ifndef ENC_DEBUG #define ENC_DEBUG 0 @@ -144,10 +145,14 @@ enc_list_update(int index, rb_raw_encoding *encoding) { RUBY_ASSERT(index < ENCODING_LIST_CAPA); - VALUE list = rb_encoding_list; + VALUE list = RUBY_ATOMIC_VALUE_LOAD(rb_encoding_list); + if (list && NIL_P(rb_ary_entry(list, index))) { + VALUE new_list = rb_ary_dup(list); + RBASIC_CLEAR_CLASS(new_list); /* initialize encoding data */ - rb_ary_store(list, index, enc_new(encoding)); + rb_ary_store(new_list, index, enc_new(encoding)); + RUBY_ATOMIC_VALUE_SET(rb_encoding_list, new_list); } } @@ -157,7 +162,7 @@ enc_list_lookup(int idx) VALUE list, enc = Qnil; if (idx < ENCODING_LIST_CAPA) { - list = rb_encoding_list; + list = RUBY_ATOMIC_VALUE_LOAD(rb_encoding_list); RUBY_ASSERT(list); enc = rb_ary_entry(list, idx); } @@ -258,6 +263,7 @@ must_encindex(int index) int rb_to_encoding_index(VALUE enc) { + ASSERT_vm_unlocking(); // can load encoding, so must not hold VM lock int idx; const char *name; @@ -667,15 +673,15 @@ int rb_enc_alias(const char *alias, const char *orig) { int idx, r; + GLOBAL_ENC_TABLE_LOCKING(enc_table) { + enc_check_addable(enc_table, alias); // can raise + } + + idx = rb_enc_find_index(orig); + if (idx < 0) return -1; GLOBAL_ENC_TABLE_LOCKING(enc_table) { - enc_check_addable(enc_table, alias); - if ((idx = rb_enc_find_index(orig)) < 0) { - r = -1; - } - else { - r = enc_alias(enc_table, alias, idx); - } + r = enc_alias(enc_table, alias, idx); } return r; @@ -742,6 +748,7 @@ int rb_require_internal_silent(VALUE fname); static int load_encoding(const char *name) { + ASSERT_vm_unlocking(); VALUE enclib = rb_sprintf("enc/%s.so", name); VALUE debug = ruby_debug; VALUE errinfo; @@ -757,7 +764,7 @@ load_encoding(const char *name) enclib = rb_fstring(enclib); ruby_debug = Qfalse; errinfo = rb_errinfo(); - loaded = rb_require_internal_silent(enclib); + loaded = rb_require_internal_silent(enclib); // must run without VM_LOCK ruby_debug = debug; rb_set_errinfo(errinfo); @@ -781,6 +788,7 @@ enc_autoload_body(rb_encoding *enc) { rb_encoding *base; int i = 0; + ASSERT_vm_unlocking(); GLOBAL_ENC_TABLE_LOCKING(enc_table) { base = enc_table->list[ENC_TO_ENCINDEX(enc)].base; @@ -792,30 +800,32 @@ enc_autoload_body(rb_encoding *enc) } } while (enc_table->list[i].enc != base && (++i, 1)); } + } + - if (i != -1) { - if (base) { - bool do_register = true; - if (rb_enc_autoload_p(base)) { - if (rb_enc_autoload(base) < 0) { - do_register = false; - i = -1; - } + if (i != -1) { + if (base) { + bool do_register = true; + if (rb_enc_autoload_p(base)) { + if (rb_enc_autoload(base) < 0) { + do_register = false; + i = -1; } + } - i = enc->ruby_encoding_index; - if (do_register) { + if (do_register) { + GLOBAL_ENC_TABLE_LOCKING(enc_table) { + i = enc->ruby_encoding_index; enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base); ((rb_raw_encoding *)enc)->ruby_encoding_index = i; } - - i &= ENC_INDEX_MASK; - } - else { - i = -2; } - } + i &= ENC_INDEX_MASK; + } + else { + i = -2; + } } return i; @@ -824,6 +834,7 @@ enc_autoload_body(rb_encoding *enc) int rb_enc_autoload(rb_encoding *enc) { + ASSERT_vm_unlocking(); int i = enc_autoload_body(enc); if (i == -2) { i = load_encoding(rb_enc_name(enc)); @@ -844,6 +855,7 @@ int rb_enc_find_index(const char *name) { int i; + ASSERT_vm_unlocking(); // it needs to be unlocked so it can call `load_encoding` if necessary GLOBAL_ENC_TABLE_LOCKING(enc_table) { i = enc_registered(enc_table, name); } @@ -1019,7 +1031,6 @@ rb_enc_associate_index(VALUE obj, int idx) rb_encoding *enc; int oldidx, oldtermlen, termlen; -/* enc_check_capable(obj);*/ rb_check_frozen(obj); oldidx = rb_enc_get_index(obj); if (oldidx == idx) @@ -1355,7 +1366,10 @@ enc_names(VALUE self) args[0] = (VALUE)rb_to_encoding_index(self); args[1] = rb_ary_new2(0); - st_foreach(global_enc_table.names, enc_names_i, (st_data_t)args); + + GLOBAL_ENC_TABLE_LOCKING(enc_table) { + st_foreach(enc_table->names, enc_names_i, (st_data_t)args); + } return args[1]; } @@ -1380,8 +1394,9 @@ enc_names(VALUE self) static VALUE enc_list(VALUE klass) { - VALUE ary = rb_ary_new2(0); - rb_ary_replace(ary, rb_encoding_list); + VALUE ary = rb_ary_new2(ENCODING_LIST_CAPA); + VALUE list = RUBY_ATOMIC_VALUE_LOAD(rb_encoding_list); + rb_ary_replace(ary, list); return ary; } @@ -1526,6 +1541,9 @@ int rb_locale_charmap_index(void); int rb_locale_encindex(void) { + // `rb_locale_charmap_index` can call `enc_find_index`, which can + // load an encoding. This needs to be done without VM lock held. + ASSERT_vm_unlocking(); int idx = rb_locale_charmap_index(); if (idx < 0) idx = ENCINDEX_UTF_8; @@ -1584,6 +1602,10 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha /* Already set */ overridden = TRUE; + if (!NIL_P(encoding)) { + enc_check_encoding(encoding); // loads it if necessary. Needs to be done outside of VM lock. + } + GLOBAL_ENC_TABLE_LOCKING(enc_table) { if (NIL_P(encoding)) { def->index = -1; @@ -1854,8 +1876,11 @@ rb_enc_name_list_i(st_data_t name, st_data_t idx, st_data_t arg) static VALUE rb_enc_name_list(VALUE klass) { - VALUE ary = rb_ary_new2(global_enc_table.names->num_entries); - st_foreach(global_enc_table.names, rb_enc_name_list_i, (st_data_t)ary); + VALUE ary; + GLOBAL_ENC_TABLE_LOCKING(enc_table) { + ary = rb_ary_new2(enc_table->names->num_entries); + st_foreach(enc_table->names, rb_enc_name_list_i, (st_data_t)ary); + } return ary; } @@ -1901,7 +1926,9 @@ rb_enc_aliases(VALUE klass) aliases[0] = rb_hash_new(); aliases[1] = rb_ary_new(); - st_foreach(global_enc_table.names, rb_enc_aliases_enc_i, (st_data_t)aliases); + GLOBAL_ENC_TABLE_LOCKING(enc_table) { + st_foreach(enc_table->names, rb_enc_aliases_enc_i, (st_data_t)aliases); + } return aliases[0]; } @@ -1969,9 +1996,9 @@ Init_Encoding(void) struct enc_table *enc_table = &global_enc_table; + rb_gc_register_address(&rb_encoding_list); list = rb_encoding_list = rb_ary_new2(ENCODING_LIST_CAPA); RBASIC_CLEAR_CLASS(list); - rb_vm_register_global_object(list); for (i = 0; i < enc_table->count; ++i) { rb_ary_push(list, enc_new(enc_table->list[i].enc)); @@ -2003,5 +2030,7 @@ Init_encodings(void) void rb_enc_foreach_name(int (*func)(st_data_t name, st_data_t idx, st_data_t arg), st_data_t arg) { - st_foreach(global_enc_table.names, func, arg); + GLOBAL_ENC_TABLE_LOCKING(enc_table) { + st_foreach(enc_table->names, func, arg); + } } diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 3003939e10f8e4..0493c8cd50856b 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -203,6 +203,18 @@ check_modifiable(struct StringIO *ptr) } } +static inline bool +outside_p(struct StringIO *ptr, long pos) +{ + return NIL_P(ptr->string) || pos >= RSTRING_LEN(ptr->string); +} + +static inline bool +eos_p(struct StringIO *ptr) +{ + return outside_p(ptr, ptr->pos); +} + static VALUE strio_s_allocate(VALUE klass) { @@ -628,9 +640,8 @@ static struct StringIO * strio_to_read(VALUE self) { struct StringIO *ptr = readable(self); - if (NIL_P(ptr->string)) return NULL; - if (ptr->pos < RSTRING_LEN(ptr->string)) return ptr; - return NULL; + if (eos_p(ptr)) return NULL; + return ptr; } /* @@ -837,7 +848,11 @@ strio_seek(int argc, VALUE *argv, VALUE self) offset = ptr->pos; break; case 2: - offset = RSTRING_LEN(ptr->string); + if (NIL_P(ptr->string)) { + offset = 0; + } else { + offset = RSTRING_LEN(ptr->string); + } break; default: error_inval("invalid whence"); @@ -906,7 +921,7 @@ strio_getc(VALUE self) int len; char *p; - if (NIL_P(str) || pos >= RSTRING_LEN(str)) { + if (eos_p(ptr)) { return Qnil; } p = RSTRING_PTR(str)+pos; @@ -927,7 +942,7 @@ strio_getbyte(VALUE self) { struct StringIO *ptr = readable(self); int c; - if (NIL_P(ptr->string) || ptr->pos >= RSTRING_LEN(ptr->string)) { + if (eos_p(ptr)) { return Qnil; } c = RSTRING_PTR(ptr->string)[ptr->pos++]; @@ -1605,10 +1620,9 @@ strio_read(int argc, VALUE *argv, VALUE self) if (len < 0) { rb_raise(rb_eArgError, "negative length %ld given", len); } - if (len > 0 && - (NIL_P(ptr->string) || ptr->pos >= RSTRING_LEN(ptr->string))) { + if (eos_p(ptr)) { if (!NIL_P(str)) rb_str_resize(str, 0); - return Qnil; + return len > 0 ? Qnil : rb_str_new(0, 0); } binary = 1; break; @@ -1684,7 +1698,7 @@ strio_pread(int argc, VALUE *argv, VALUE self) struct StringIO *ptr = readable(self); - if (offset >= RSTRING_LEN(ptr->string)) { + if (outside_p(ptr, offset)) { rb_eof_error(); } @@ -1921,7 +1935,7 @@ Init_stringio(void) #undef rb_intern #ifdef HAVE_RB_EXT_RACTOR_SAFE - rb_ext_ractor_safe(true); + rb_ext_ractor_safe(true); #endif VALUE StringIO = rb_define_class("StringIO", rb_cObject); diff --git a/gc.c b/gc.c index 3d0935ad1a4f10..7663e82f41b5ab 100644 --- a/gc.c +++ b/gc.c @@ -1975,14 +1975,17 @@ object_id_to_ref(void *objspace_ptr, VALUE object_id) // GC Must not trigger while we build the table, otherwise if we end // up freeing an object that had an ID, we might try to delete it from // the table even though it wasn't inserted yet. - id2ref_tbl = st_init_table(&object_id_hash_type); - id2ref_value = TypedData_Wrap_Struct(0, &id2ref_tbl_type, id2ref_tbl); + st_table *tmp_id2ref_tbl = st_init_table(&object_id_hash_type); + VALUE tmp_id2ref_value = TypedData_Wrap_Struct(0, &id2ref_tbl_type, tmp_id2ref_tbl); // build_id2ref_i will most certainly malloc, which could trigger GC and sweep // objects we just added to the table. // By calling rb_gc_disable() we also save having to handle potentially garbage objects. bool gc_disabled = RTEST(rb_gc_disable()); { + id2ref_tbl = tmp_id2ref_tbl; + id2ref_value = tmp_id2ref_value; + rb_gc_impl_each_object(objspace, build_id2ref_i, (void *)id2ref_tbl); } if (!gc_disabled) rb_gc_enable(); @@ -4354,7 +4357,7 @@ gc_config_set(rb_execution_context_t *ec, VALUE self, VALUE hash) rb_gc_impl_config_set(objspace, hash); - return rb_gc_impl_config_get(objspace); + return Qnil; } static VALUE diff --git a/gc.rb b/gc.rb index 1298e3005660b6..603520df5312ab 100644 --- a/gc.rb +++ b/gc.rb @@ -258,83 +258,71 @@ def self.stat_heap heap_name = nil, hash_or_key = nil # call-seq: # GC.config -> hash - # GC.config(hash) -> hash + # GC.config(hash_to_merge) -> merged_hash # - # Sets or gets information about the current \GC config. + # This method is implementation-specific to CRuby. # - # Configuration parameters are \GC implementation-specific and may change - # without notice. + # Sets or gets information about the current \GC configuration. # - # This method can be called without parameters to retrieve the current config - # as a +Hash+ with +Symbol+ keys. + # Configuration parameters are \GC implementation-specific and may change without notice. # - # This method can also be called with a +Hash+ argument to assign values to - # valid config keys. Config keys missing from the passed +Hash+ will be left - # unmodified. + # With no argument given, returns a hash containing the configuration: # - # If a key/value pair is passed to this function that does not correspond to - # a valid config key for the \GC implementation being used, no config will be - # updated, the key will be present in the returned Hash, and its value will - # be +nil+. This is to facilitate easy migration between \GC implementations. + # GC.config + # # => {rgengc_allow_full_mark: true, implementation: "default"} # - # In both call-seqs, the return value of GC.config will be a +Hash+ - # containing the most recent full configuration, i.e., all keys and values - # defined by the specific \GC implementation being used. In the case of a - # config update, the return value will include the new values being updated. + # With argument +hash_to_merge+ given, + # merges that hash into the stored configuration hash; + # ignores unknown hash keys; + # returns the implementation-specific configuration hash (see below): # - # This method is only expected to work on CRuby. - # - # === \GC Implementation independent values - # - # The GC.config hash can also contain keys that are global and - # read-only. These keys are not specific to any one \GC library implementation - # and attempting to write to them will raise +ArgumentError+. + # GC.config(rgengc_allow_full_mark: false) + # # => {rgengc_allow_full_mark: false} + # GC.config + # # => {rgengc_allow_full_mark: false, implementation: "default"} + # GC.config(foo: 'bar') + # # => {rgengc_allow_full_mark: false} + # GC.config + # # => {rgengc_allow_full_mark: false, implementation: "default"} # - # There is currently only one global, read-only key: + # All-Implementations Configuration # - # [implementation] - # Returns a +String+ containing the name of the currently loaded \GC library, - # if one has been loaded using +RUBY_GC_LIBRARY+, and "default" in all other - # cases + # The single read-only entry for all implementations is: # - # === \GC Implementation specific values + # - +implementation+: + # the string name of the implementation; + # for the Ruby default implementation, 'default'. # - # \GC libraries are expected to document their own configuration. Valid keys - # for Ruby's default \GC implementation are: + # Implementation-Specific Configuration # - # [rgengc_allow_full_mark] - # Controls whether the \GC is allowed to run a full mark (young & old objects). + # A \GC implementation maintains its own implementation-specific configuration. # - # When +true+, \GC interleaves major and minor collections. This is the default. \GC - # will function as intended. + # For Ruby's default implementation the single entry is: # - # When +false+, the \GC will never trigger a full marking cycle unless - # explicitly requested by user code. Instead, only a minor mark will run— - # only young objects will be marked. When the heap space is exhausted, new - # pages will be allocated immediately instead of running a full mark. + # - +rgengc_allow_full_mark+: + # Controls whether the \GC is allowed to run a full mark (young & old objects): # - # A flag will be set to notify that a full mark has been - # requested. This flag is accessible using - # GC.latest_gc_info(:need_major_by) + # - +true+ (default): \GC interleaves major and minor collections. + # A flag is set to notify GC that a full mark has been requested. + # This flag is accessible via GC.latest_gc_info(:need_major_by). + # - +false+: \GC does not initiate a full marking cycle unless explicitly directed by user code; + # see GC.start. + # Setting this parameter to +false+ disables young-to-old promotion. + # For performance reasons, we recommended warming up the application using Process.warmup + # before setting this parameter to +false+. # - # The user can trigger a major collection at any time using - # GC.start(full_mark: true) - # - # When +false+, Young to Old object promotion is disabled. For performance - # reasons, it is recommended to warm up an application using +Process.warmup+ - # before setting this parameter to +false+. def self.config hash = nil - return Primitive.gc_config_get unless hash - - if(Primitive.cexpr!("RBOOL(RB_TYPE_P(hash, T_HASH))")) + if Primitive.cexpr!("RBOOL(RB_TYPE_P(hash, T_HASH))") if hash.include?(:implementation) raise ArgumentError, 'Attempting to set read-only key "Implementation"' end Primitive.gc_config_set hash - else + elsif hash != nil raise ArgumentError end + + Primitive.gc_config_get end # call-seq: diff --git a/hash.c b/hash.c index 7ce1b768e0e831..de9bc97ea69cdf 100644 --- a/hash.c +++ b/hash.c @@ -5192,25 +5192,26 @@ env_enc_str_new(const char *ptr, long len, rb_encoding *enc) } static VALUE -env_str_new(const char *ptr, long len) +env_str_new(const char *ptr, long len, rb_encoding *enc) { - return env_enc_str_new(ptr, len, env_encoding()); + return env_enc_str_new(ptr, len, enc); } static VALUE -env_str_new2(const char *ptr) +env_str_new2(const char *ptr, rb_encoding *enc) { if (!ptr) return Qnil; - return env_str_new(ptr, strlen(ptr)); + return env_str_new(ptr, strlen(ptr), enc); } static VALUE getenv_with_lock(const char *name) { VALUE ret; + rb_encoding *enc = env_encoding(); ENV_LOCKING() { const char *val = getenv(name); - ret = env_str_new2(val); + ret = env_str_new2(val, enc); } return ret; } @@ -5773,13 +5774,14 @@ env_values(void) { VALUE ary = rb_ary_new(); + rb_encoding *enc = env_encoding(); ENV_LOCKING() { char **env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { - rb_ary_push(ary, env_str_new2(s+1)); + rb_ary_push(ary, env_str_new2(s+1, enc)); } env++; } @@ -5865,14 +5867,15 @@ env_each_pair(VALUE ehash) VALUE ary = rb_ary_new(); + rb_encoding *enc = env_encoding(); ENV_LOCKING() { char **env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { - rb_ary_push(ary, env_str_new(*env, s-*env)); - rb_ary_push(ary, env_str_new2(s+1)); + rb_ary_push(ary, env_str_new(*env, s-*env, enc)); + rb_ary_push(ary, env_str_new2(s+1, enc)); } env++; } @@ -6255,13 +6258,14 @@ env_to_a(VALUE _) { VALUE ary = rb_ary_new(); + rb_encoding *enc = env_encoding(); ENV_LOCKING() { char **env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { - rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s-*env), - env_str_new2(s+1))); + rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s-*env, enc), + env_str_new2(s+1, enc))); } env++; } @@ -6509,6 +6513,7 @@ env_key(VALUE dmy, VALUE value) StringValue(value); VALUE str = Qnil; + rb_encoding *enc = env_encoding(); ENV_LOCKING() { char **env = GET_ENVIRON(environ); while (*env) { @@ -6516,7 +6521,7 @@ env_key(VALUE dmy, VALUE value) if (s++) { long len = strlen(s); if (RSTRING_LEN(value) == len && strncmp(s, RSTRING_PTR(value), len) == 0) { - str = env_str_new(*env, s-*env-1); + str = env_str_new(*env, s-*env-1, enc); break; } } @@ -6533,13 +6538,14 @@ env_to_hash(void) { VALUE hash = rb_hash_new(); + rb_encoding *enc = env_encoding(); ENV_LOCKING() { char **env = GET_ENVIRON(environ); while (*env) { char *s = strchr(*env, '='); if (s) { - rb_hash_aset(hash, env_str_new(*env, s-*env), - env_str_new2(s+1)); + rb_hash_aset(hash, env_str_new(*env, s-*env, enc), + env_str_new2(s+1, enc)); } env++; } @@ -6684,14 +6690,15 @@ env_shift(VALUE _) VALUE result = Qnil; VALUE key = Qnil; + rb_encoding *enc = env_encoding(); ENV_LOCKING() { char **env = GET_ENVIRON(environ); if (*env) { const char *p = *env; char *s = strchr(p, '='); if (s) { - key = env_str_new(p, s-p); - VALUE val = env_str_new2(getenv(RSTRING_PTR(key))); + key = env_str_new(p, s-p, enc); + VALUE val = env_str_new2(getenv(RSTRING_PTR(key)), enc); result = rb_assoc_new(key, val); } } diff --git a/node_dump.c b/node_dump.c index c318baeeede009..18ac3d7b35b4e5 100644 --- a/node_dump.c +++ b/node_dump.c @@ -309,8 +309,11 @@ dump_node(VALUE buf, VALUE indent, int comment, const NODE * node) ANN("example: case x; in 1; foo; in 2; bar; else baz; end"); F_NODE(nd_head, RNODE_IN, "in pattern"); F_NODE(nd_body, RNODE_IN, "in body"); - LAST_NODE; F_NODE(nd_next, RNODE_IN, "next in clause"); + F_LOC(in_keyword_loc, RNODE_IN); + F_LOC(then_keyword_loc, RNODE_IN); + LAST_NODE; + F_LOC(operator_loc, RNODE_IN); return; case NODE_WHILE: diff --git a/parse.y b/parse.y index e77dc790bcd1cd..c0f46a395f9f59 100644 --- a/parse.y +++ b/parse.y @@ -1070,7 +1070,7 @@ static rb_node_case_t *rb_node_case_new(struct parser_params *p, NODE *nd_head, static rb_node_case2_t *rb_node_case2_new(struct parser_params *p, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *case_keyword_loc, const YYLTYPE *end_keyword_loc); static rb_node_case3_t *rb_node_case3_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, const YYLTYPE *loc, const YYLTYPE *case_keyword_loc, const YYLTYPE *end_keyword_loc); static rb_node_when_t *rb_node_when_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc, const YYLTYPE *keyword_loc, const YYLTYPE *then_keyword_loc); -static rb_node_in_t *rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc); +static rb_node_in_t *rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc, const YYLTYPE *in_keyword_loc, const YYLTYPE *then_keyword_loc, const YYLTYPE *operator_loc); static rb_node_while_t *rb_node_while_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, long nd_state, const YYLTYPE *loc, const YYLTYPE *keyword_loc, const YYLTYPE *closing_loc); static rb_node_until_t *rb_node_until_new(struct parser_params *p, NODE *nd_cond, NODE *nd_body, long nd_state, const YYLTYPE *loc, const YYLTYPE *keyword_loc, const YYLTYPE *closing_loc); static rb_node_iter_t *rb_node_iter_new(struct parser_params *p, rb_node_args_t *nd_args, NODE *nd_body, const YYLTYPE *loc); @@ -1178,7 +1178,7 @@ static rb_node_error_t *rb_node_error_new(struct parser_params *p, const YYLTYPE #define NEW_CASE2(b,loc,ck_loc,ek_loc) (NODE *)rb_node_case2_new(p,b,loc,ck_loc,ek_loc) #define NEW_CASE3(h,b,loc,ck_loc,ek_loc) (NODE *)rb_node_case3_new(p,h,b,loc,ck_loc,ek_loc) #define NEW_WHEN(c,t,e,loc,k_loc,t_loc) (NODE *)rb_node_when_new(p,c,t,e,loc,k_loc,t_loc) -#define NEW_IN(c,t,e,loc) (NODE *)rb_node_in_new(p,c,t,e,loc) +#define NEW_IN(c,t,e,loc,ik_loc,tk_loc,o_loc) (NODE *)rb_node_in_new(p,c,t,e,loc,ik_loc,tk_loc,o_loc) #define NEW_WHILE(c,b,n,loc,k_loc,c_loc) (NODE *)rb_node_while_new(p,c,b,n,loc,k_loc,c_loc) #define NEW_UNTIL(c,b,n,loc,k_loc,c_loc) (NODE *)rb_node_until_new(p,c,b,n,loc,k_loc,c_loc) #define NEW_ITER(a,b,loc) (NODE *)rb_node_iter_new(p,a,b,loc) @@ -3472,7 +3472,7 @@ expr : command_call pop_pktbl(p, $p_pktbl); pop_pvtbl(p, $p_pvtbl); p->ctxt.in_kwarg = $ctxt.in_kwarg; - $$ = NEW_CASE3($arg, NEW_IN($body, 0, 0, &@body), &@$, &NULL_LOC, &NULL_LOC); + $$ = NEW_CASE3($arg, NEW_IN($body, 0, 0, &@body, &NULL_LOC, &NULL_LOC, &@2), &@$, &NULL_LOC, &NULL_LOC); /*% ripper: case!($:arg, in!($:body, Qnil, Qnil)) %*/ } | arg keyword_in @@ -3485,7 +3485,7 @@ expr : command_call pop_pktbl(p, $p_pktbl); pop_pvtbl(p, $p_pvtbl); p->ctxt.in_kwarg = $ctxt.in_kwarg; - $$ = NEW_CASE3($arg, NEW_IN($body, NEW_TRUE(&@body), NEW_FALSE(&@body), &@body), &@$, &NULL_LOC, &NULL_LOC); + $$ = NEW_CASE3($arg, NEW_IN($body, NEW_TRUE(&@body), NEW_FALSE(&@body), &@body, &@keyword_in, &NULL_LOC, &NULL_LOC), &@$, &NULL_LOC, &NULL_LOC); /*% ripper: case!($:arg, in!($:body, Qnil, Qnil)) %*/ } | arg %prec tLBRACE_ARG @@ -5399,7 +5399,7 @@ p_case_body : keyword_in compstmt(stmts) p_cases[cases] { - $$ = NEW_IN($expr, $compstmt, $cases, &@$); + $$ = NEW_IN($expr, $compstmt, $cases, &@$, &@keyword_in, &@then, &NULL_LOC); /*% ripper: in!($:expr, $:compstmt, $:cases) %*/ } ; @@ -11528,12 +11528,15 @@ rb_node_when_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd } static rb_node_in_t * -rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc) +rb_node_in_new(struct parser_params *p, NODE *nd_head, NODE *nd_body, NODE *nd_next, const YYLTYPE *loc, const YYLTYPE *in_keyword_loc, const YYLTYPE *then_keyword_loc, const YYLTYPE *operator_loc) { rb_node_in_t *n = NODE_NEWNODE(NODE_IN, rb_node_in_t, loc); n->nd_head = nd_head; n->nd_body = nd_body; n->nd_next = nd_next; + n->in_keyword_loc = *in_keyword_loc; + n->then_keyword_loc = *then_keyword_loc; + n->operator_loc = *operator_loc; return n; } diff --git a/rubyparser.h b/rubyparser.h index e436d1c404b2a2..cc63efd3f85998 100644 --- a/rubyparser.h +++ b/rubyparser.h @@ -324,6 +324,9 @@ typedef struct RNode_IN { struct RNode *nd_head; struct RNode *nd_body; struct RNode *nd_next; + rb_code_location_t in_keyword_loc; + rb_code_location_t then_keyword_loc; + rb_code_location_t operator_loc; } rb_node_in_t; typedef struct RNode_LOOP { diff --git a/string.c b/string.c index fe848d6a4a821c..96a9f96bd3432b 100644 --- a/string.c +++ b/string.c @@ -4381,9 +4381,9 @@ str_casecmp(VALUE str1, VALUE str2) p2 += l2; } } - if (RSTRING_LEN(str1) == RSTRING_LEN(str2)) return INT2FIX(0); - if (RSTRING_LEN(str1) > RSTRING_LEN(str2)) return INT2FIX(1); - return INT2FIX(-1); + if (p1 == p1end && p2 == p2end) return INT2FIX(0); + if (p1 == p1end) return INT2FIX(-1); + return INT2FIX(1); } /* diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb index 6372b0d34e5e71..9a7d75c270b661 100644 --- a/test/ruby/test_ast.rb +++ b/test/ruby/test_ast.rb @@ -1514,6 +1514,20 @@ def test_if_locations assert_locations(node.children[-1].children[1].children[0].locations, [[1, 11, 1, 17], [1, 13, 1, 15], nil, nil]) end + def test_in_locations + node = ast_parse("case 1; in 2 then 3; end") + assert_locations(node.children[-1].children[1].locations, [[1, 8, 1, 20], [1, 8, 1, 10], [1, 13, 1, 17], nil]) + + node = ast_parse("1 => a") + assert_locations(node.children[-1].children[1].locations, [[1, 5, 1, 6], nil, nil, [1, 2, 1, 4]]) + + node = ast_parse("1 in a") + assert_locations(node.children[-1].children[1].locations, [[1, 5, 1, 6], [1, 2, 1, 4], nil, nil]) + + node = ast_parse("case 1; in 2; 3; end") + assert_locations(node.children[-1].children[1].locations, [[1, 8, 1, 16], [1, 8, 1, 10], [1, 12, 1, 13], nil]) + end + def test_next_locations node = ast_parse("loop { next 1 }") assert_locations(node.children[-1].children[-1].children[-1].locations, [[1, 7, 1, 13], [1, 7, 1, 11]]) diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb index 85022cbc4d8924..ccccd212b692eb 100644 --- a/test/ruby/test_gc.rb +++ b/test/ruby/test_gc.rb @@ -75,12 +75,9 @@ def test_gc_config_setting_returns_updated_config_hash GC.start end - def test_gc_config_setting_returns_nil_for_missing_keys - missing_value = GC.config(no_such_key: true)[:no_such_key] - assert_nil(missing_value) - ensure - GC.config(full_mark: true) - GC.start + def test_gc_config_setting_returns_config_hash + hash = GC.config(no_such_key: true) + assert_equal(GC.config, hash) end def test_gc_config_disable_major diff --git a/test/ruby/test_objectspace.rb b/test/ruby/test_objectspace.rb index f27f586ab79fca..a479547599a03a 100644 --- a/test/ruby/test_objectspace.rb +++ b/test/ruby/test_objectspace.rb @@ -284,6 +284,21 @@ def test_each_object_recursive_key end; end + def test_id2ref_table_build + assert_separately([], <<-End) + 10.times do + Object.new.object_id + end + + GC.start(immediate_mark: false) + + obj = Object.new + EnvUtil.suppress_warning do + assert_equal obj, ObjectSpace._id2ref(obj.object_id) + end + End + end + def test_each_object_singleton_class assert_separately([], <<-End) class C diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 8fb57bd58e6111..c7e4b0c1ec7fee 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -2832,16 +2832,32 @@ def test_compare_different_encoding_string def test_casecmp assert_equal(0, S("FoO").casecmp("fOO")) assert_equal(1, S("FoO").casecmp("BaR")) + assert_equal(-1, S("foo").casecmp("FOOBAR")) assert_equal(-1, S("baR").casecmp("FoO")) assert_equal(1, S("\u3042B").casecmp("\u3042a")) assert_equal(-1, S("foo").casecmp("foo\0")) + assert_equal(1, S("FOOBAR").casecmp("foo")) + assert_equal(0, S("foo\0bar").casecmp("FOO\0BAR")) assert_nil(S("foo").casecmp(:foo)) assert_nil(S("foo").casecmp(Object.new)) + assert_nil(S("foo").casecmp(0)) + assert_nil(S("foo").casecmp(5.00)) + o = Object.new def o.to_str; "fOO"; end assert_equal(0, S("FoO").casecmp(o)) + + assert_equal(0, S("#" * 128 + "A" * 256 + "b").casecmp("#" * 128 + "a" * 256 + "B")) + assert_equal(0, S("a" * 256 + "B").casecmp("A" * 256 + "b")) + + assert_equal(-1, S("@").casecmp("`")) + assert_equal(0, S("hello\u00E9X").casecmp("HELLO\u00E9x")) + + s1 = S("\xff".force_encoding("UTF-8")) + s2 = S("\xff".force_encoding("ISO-2022-JP")) + assert_nil(s1.casecmp(s2)) end def test_casecmp? @@ -2854,9 +2870,16 @@ def test_casecmp? assert_nil(S("foo").casecmp?(:foo)) assert_nil(S("foo").casecmp?(Object.new)) + assert_nil(S("foo").casecmp(0)) + assert_nil(S("foo").casecmp(5.00)) + o = Object.new def o.to_str; "fOO"; end assert_equal(true, S("FoO").casecmp?(o)) + + s1 = S("\xff".force_encoding("UTF-8")) + s2 = S("\xff".force_encoding("ISO-2022-JP")) + assert_nil(s1.casecmp?(s2)) end def test_upcase2 diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb index 63d37f4ba4ff90..2b6f8234ced64f 100644 --- a/test/ruby/test_transcode.rb +++ b/test/ruby/test_transcode.rb @@ -2320,6 +2320,46 @@ def test_newline_options assert_equal("A\nB\nC", s.encode(usascii, newline: :lf)) end + def test_ractor_lazy_load_encoding + assert_ractor("#{<<~"begin;"}\n#{<<~'end;'}") + begin; + rs = [] + autoload_encodings = Encoding.list.select { |e| e.inspect.include?("(autoload)") }.freeze + 7.times do + rs << Ractor.new(autoload_encodings) do |encodings| + str = "\u0300" + encodings.each do |enc| + str.encode(enc) rescue Encoding::UndefinedConversionError + end + end + end + + while rs.any? + r, _obj = Ractor.select(*rs) + rs.delete(r) + end + assert rs.empty? + end; + end + + def test_ractor_lazy_load_encoding_random + assert_ractor("#{<<~"begin;"}\n#{<<~'end;'}") + begin; + rs = [] + 100.times do + rs << Ractor.new do + "\u0300".encode(Encoding.list.sample) rescue Encoding::UndefinedConversionError + end + end + + while rs.any? + r, _obj = Ractor.select(*rs) + rs.delete(r) + end + assert rs.empty? + end; + end + private def assert_conversion_both_ways_utf8(utf8, raw, encoding) diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb index 002b946b6f1b58..5215a6d31273dd 100644 --- a/test/stringio/test_stringio.rb +++ b/test/stringio/test_stringio.rb @@ -70,6 +70,31 @@ def test_null assert_nil io.getc end + def test_pread_eof + io = StringIO.new(nil) + assert_predicate io, :eof? + end + + def test_pread_null + io = StringIO.new(nil) + assert_raise(EOFError) { io.pread(1, 0) } + end + + def test_read_null + io = StringIO.new(nil) + assert_equal "", io.read(0) + end + + def test_seek_null + io = StringIO.new(nil) + assert_equal(0, io.seek(0, IO::SEEK_SET)) + assert_equal(0, io.pos) + assert_equal(0, io.seek(0, IO::SEEK_CUR)) + assert_equal(0, io.pos) + assert_equal(0, io.seek(0, IO::SEEK_END)) # This should not segfault + assert_equal(0, io.pos) + end + def test_truncate io = StringIO.new("") io.puts "abc" diff --git a/time.c b/time.c index 7159a9309840de..95df85db9359a4 100644 --- a/time.c +++ b/time.c @@ -1891,8 +1891,8 @@ static void time_mark_and_move(void *ptr) { struct time_object *tobj = ptr; - if (!FIXWV_P(tobj->timew)) { - rb_gc_mark_and_move(&WIDEVAL_GET(tobj->timew)); + if (!WIDEVALUE_IS_WIDER || !FIXWV_P(tobj->timew)) { + rb_gc_mark_and_move((VALUE *)&WIDEVAL_GET(tobj->timew)); } rb_gc_mark_and_move(&tobj->vtm.year); rb_gc_mark_and_move(&tobj->vtm.subsecx); diff --git a/transcode.c b/transcode.c index d8cd90e56d5cf7..507bce78e1357f 100644 --- a/transcode.c +++ b/transcode.c @@ -340,7 +340,7 @@ transcode_search_path(const char *sname, const char *dname, bfs.queue_last_ptr = &q->next; bfs.queue = q; - bfs.visited = st_init_strcasetable(); + bfs.visited = st_init_strcasetable(); // due to base encodings, we need to do search in a loop st_add_direct(bfs.visited, (st_data_t)sname, (st_data_t)NULL); RB_VM_LOCKING() { @@ -351,14 +351,14 @@ transcode_search_path(const char *sname, const char *dname, bfs.queue_last_ptr = &bfs.queue; } - lookup_res = st_lookup(transcoder_table, (st_data_t)q->enc, &val); + lookup_res = st_lookup(transcoder_table, (st_data_t)q->enc, &val); // src => table2 if (!lookup_res) { xfree(q); continue; } table2 = (st_table *)val; - if (st_lookup(table2, (st_data_t)dname, &val)) { + if (st_lookup(table2, (st_data_t)dname, &val)) { // dest => econv st_add_direct(bfs.visited, (st_data_t)dname, (st_data_t)q->enc); xfree(q); found = true; @@ -411,8 +411,7 @@ int rb_require_internal_silent(VALUE fname); static const rb_transcoder * load_transcoder_entry(transcoder_entry_t *entry) { - // changes result of entry->transcoder depending on if it's required or not, so needs lock - ASSERT_vm_locking(); + ASSERT_vm_unlocking(); if (entry->transcoder) return entry->transcoder; @@ -427,7 +426,7 @@ load_transcoder_entry(transcoder_entry_t *entry) memcpy(path + sizeof(transcoder_lib_prefix) - 1, lib, len); rb_str_set_len(fn, total_len); OBJ_FREEZE(fn); - rb_require_internal_silent(fn); + rb_require_internal_silent(fn); // Sets entry->transcoder } if (entry->transcoder) @@ -981,7 +980,6 @@ rb_econv_open_by_transcoder_entries(int n, transcoder_entry_t **entries) { rb_econv_t *ec; int i, ret; - ASSERT_vm_locking(); for (i = 0; i < n; i++) { const rb_transcoder *tr; @@ -1026,10 +1024,8 @@ rb_econv_open0(const char *sname, const char *dname, int ecflags) transcoder_entry_t **entries = NULL; int num_trans; rb_econv_t *ec; - ASSERT_vm_locking(); - /* Just check if sname and dname are defined */ - /* (This check is needed?) */ + // loads encodings if not loaded already if (*sname) rb_enc_find_index(sname); if (*dname) rb_enc_find_index(dname); @@ -1117,15 +1113,13 @@ rb_econv_open(const char *sname, const char *dname, int ecflags) if (num_decorators == -1) return NULL; - RB_VM_LOCKING() { - ec = rb_econv_open0(sname, dname, ecflags & ECONV_ERROR_HANDLER_MASK); - if (ec) { - for (i = 0; i < num_decorators; i++) { - if (rb_econv_decorate_at_last(ec, decorators[i]) == -1) { - rb_econv_close(ec); - ec = NULL; - break; - } + ec = rb_econv_open0(sname, dname, ecflags & ECONV_ERROR_HANDLER_MASK); + if (ec) { + for (i = 0; i < num_decorators; i++) { + if (rb_econv_decorate_at_last(ec, decorators[i]) == -1) { + rb_econv_close(ec); + ec = NULL; + break; } } } @@ -1960,12 +1954,9 @@ rb_econv_add_converter(rb_econv_t *ec, const char *sname, const char *dname, int if (ec->started != 0) return -1; - RB_VM_LOCKING() { - entry = get_transcoder_entry(sname, dname); - if (entry) { - tr = load_transcoder_entry(entry); - } - + entry = get_transcoder_entry(sname, dname); + if (entry) { + tr = load_transcoder_entry(entry); } return tr ? rb_econv_add_transcoder_at(ec, tr, n) : -1; @@ -2681,21 +2672,19 @@ rb_econv_open_opts(const char *source_encoding, const char *destination_encoding replacement = rb_hash_aref(opthash, sym_replace); } - RB_VM_LOCKING() { - ec = rb_econv_open(source_encoding, destination_encoding, ecflags); - if (ec) { - if (!NIL_P(replacement)) { - int ret; - rb_encoding *enc = rb_enc_get(replacement); - - ret = rb_econv_set_replacement(ec, - (const unsigned char *)RSTRING_PTR(replacement), - RSTRING_LEN(replacement), - rb_enc_name(enc)); - if (ret == -1) { - rb_econv_close(ec); - ec = NULL; - } + ec = rb_econv_open(source_encoding, destination_encoding, ecflags); + if (ec) { + if (!NIL_P(replacement)) { + int ret; + rb_encoding *enc = rb_enc_get(replacement); + + ret = rb_econv_set_replacement(ec, + (const unsigned char *)RSTRING_PTR(replacement), + RSTRING_LEN(replacement), + rb_enc_name(enc)); + if (ret == -1) { + rb_econv_close(ec); + ec = NULL; } } } @@ -3132,10 +3121,8 @@ decorate_convpath(VALUE convpath, int ecflags) const char *dname = rb_enc_name(rb_to_encoding(RARRAY_AREF(pair, 1))); transcoder_entry_t *entry; const rb_transcoder *tr; - RB_VM_LOCKING() { - entry = get_transcoder_entry(sname, dname); - tr = load_transcoder_entry(entry); - } + entry = get_transcoder_entry(sname, dname); + tr = load_transcoder_entry(entry); if (!tr) return -1; if (!DECORATOR_P(tr->src_encoding, tr->dst_encoding) && diff --git a/win32/vssetup.cmd b/win32/vssetup.cmd index be77c87b294a6c..1ff0a7d10ab6f4 100755 --- a/win32/vssetup.cmd +++ b/win32/vssetup.cmd @@ -1,27 +1,27 @@ -@echo off -setlocal ENABLEEXTENSIONS +@setlocal ENABLEEXTENSIONS +::- do not `echo off` that affects the called batch files ::- check for vswhere -set vswhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe -if not exist "%vswhere%" ( +@set vswhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe +@if not exist "%vswhere%" ( echo 1>&2 vswhere.exe not found exit /b 1 ) ::- find the latest build tool and its setup batch file. -set VSDEVCMD= -for /f "delims=" %%I in ('"%vswhere%" -products * -latest -property installationPath') do ( +@set VSDEVCMD= +@for /f "delims=" %%I in ('"%vswhere%" -products * -latest -property installationPath') do @( set VSDEVCMD=%%I\Common7\Tools\VsDevCmd.bat ) -if not defined VSDEVCMD ( +@if not defined VSDEVCMD ( echo 1>&2 Visual Studio not found exit /b 1 ) ::- default to the current processor. -set arch=%PROCESSOR_ARCHITECTURE% +@set arch=%PROCESSOR_ARCHITECTURE% ::- `vsdevcmd.bat` requires arch names to be lowercase -for %%i in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do @( +@for %%i in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do @( call set arch=%%arch:%%i=%%i%% ) -echo on && endlocal && "%VSDEVCMD%" -arch=%arch% -host_arch=%arch% %* +@(endlocal && "%VSDEVCMD%" -arch=%arch% -host_arch=%arch% %*)