diff --git a/.github/workflows/compilers.yml b/.github/workflows/compilers.yml index 06fba65b4e459f..c00674c410529d 100644 --- a/.github/workflows/compilers.yml +++ b/.github/workflows/compilers.yml @@ -238,7 +238,7 @@ jobs: - run: id working-directory: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: sparse-checkout-cone-mode: false sparse-checkout: /.github diff --git a/bootstraptest/test_fork.rb b/bootstraptest/test_fork.rb index 9e64f1d026e2cf..0cdfb5ab24c347 100644 --- a/bootstraptest/test_fork.rb +++ b/bootstraptest/test_fork.rb @@ -76,6 +76,10 @@ }, '[ruby-dev:44005] [Ruby 1.9 - Bug #4950]' assert_equal 'ok', %q{ + # This test is very unstable. It fails a Ractor assertion: + # Assertion Failed: ../src/thread_pthread.c:451:ractor_sched_set_locked:vm->ractor.sched.lock_owner == NULL + skip :ok # too unstable + def now = Process.clock_gettime(Process::CLOCK_MONOTONIC) Thread.new do diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 15166d7c5dadc2..31b9aabf11b7f2 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -4510,3 +4510,10 @@ def compiled_method(is_private) :ok RUBY + +assert_normal_exit %{ + class Bug20997 + def foo(&) = self.class.name(&) + new.foo + end +} diff --git a/class.c b/class.c index 0fd4029f4c84bf..b2a219100cc8b0 100644 --- a/class.c +++ b/class.c @@ -1194,8 +1194,8 @@ rb_include_module(VALUE klass, VALUE module) iclass = iclass->next; } - int do_include = 1; while (iclass) { + int do_include = 1; VALUE check_class = iclass->klass; /* During lazy sweeping, iclass->klass could be a dead object that * has not yet been swept. */ diff --git a/common.mk b/common.mk index c8e9685b933388..70b66b11f411f0 100644 --- a/common.mk +++ b/common.mk @@ -1560,7 +1560,7 @@ no-test-bundled-gems-prepare: no-test-bundled-gems-precheck yes-test-bundled-gems-prepare: yes-test-bundled-gems-precheck $(ACTIONS_GROUP) $(XRUBY) -C "$(srcdir)" bin/gem install --no-document \ - --install-dir .bundle --conservative "hoe" "json-schema" "test-unit-rr" + --install-dir .bundle --conservative "hoe" "json-schema:5.1.0" "test-unit-rr" $(ACTIONS_ENDGROUP) PREPARE_BUNDLED_GEMS = test-bundled-gems-prepare diff --git a/compile.c b/compile.c index 03dd85a6297b79..f9738867b727b9 100644 --- a/compile.c +++ b/compile.c @@ -9670,7 +9670,8 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node /* optimization shortcut * obj["literal"] = value -> opt_aset_with(obj, "literal", value) */ - if (mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args && + if (!ISEQ_COMPILE_DATA(iseq)->in_masgn && + mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args && nd_type_p(RNODE_ATTRASGN(node)->nd_args, NODE_LIST) && RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->as.nd_alen == 2 && nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_STR) && ISEQ_COMPILE_DATA(iseq)->current_block == NULL && @@ -9868,7 +9869,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_MASGN:{ + bool prev_in_masgn = ISEQ_COMPILE_DATA(iseq)->in_masgn; + ISEQ_COMPILE_DATA(iseq)->in_masgn = true; compile_massign(iseq, ret, node, popped); + ISEQ_COMPILE_DATA(iseq)->in_masgn = prev_in_masgn; break; } diff --git a/coroutine/asyncify/Context.h b/coroutine/asyncify/Context.h index 7dba829a1dfd50..71791a400492b5 100644 --- a/coroutine/asyncify/Context.h +++ b/coroutine/asyncify/Context.h @@ -13,6 +13,7 @@ #include #include +#include #include "wasm/asyncify.h" #include "wasm/machine.h" #include "wasm/fiber.h" @@ -47,10 +48,13 @@ static inline void coroutine_initialize_main(struct coroutine_context * context) static inline void coroutine_initialize(struct coroutine_context *context, coroutine_start start, void *stack, size_t size) { - if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)stack + size); + // Linear stack pointer must be always aligned down to 16 bytes. + // https://github.com/WebAssembly/tool-conventions/blob/c74267a5897c1bdc9aa60adeaf41816387d3cd12/BasicCABI.md#the-linear-stack + uintptr_t sp = ((uintptr_t)stack + size) & ~0xF; + if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)sp); rb_wasm_init_context(&context->fc, coroutine_trampoline, start, context); // record the initial stack pointer position to restore it after resumption - context->current_sp = (char *)stack + size; + context->current_sp = (char *)sp; context->stack_base = stack; context->size = size; } diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in index 192a8cc7112b3c..7bcf1ada30cadd 100644 --- a/cygwin/GNUmakefile.in +++ b/cygwin/GNUmakefile.in @@ -2,6 +2,9 @@ gnumake = yes include Makefile +MUNICODE_FLAG := $(if $(filter mingw%,$(target_os)),-municode) +override EXE_LDFLAGS += $(MUNICODE_FLAG) + DLLWRAP = @DLLWRAP@ --target=$(target_os) --driver-name="$(CC)" ifeq (@USE_LLVM_WINDRES@,yes) # USE_LLVM_WINDRES # llvm-windres fails when preprocessor options are added @@ -69,7 +72,7 @@ $(PROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT) $(WPROGRAM): $(RUBYW_INSTALL_NAME).res.$(OBJEXT) @rm -f $@ $(ECHO) linking $@ - $(Q) $(PURIFY) $(CC) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \ + $(Q) $(PURIFY) $(CC) $(MUNICODE_FLAG) -mwindows -e $(SYMBOL_PREFIX)mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \ $(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(LIBS) -o $@ $(STUBPROGRAM): $(RUBY_INSTALL_NAME).res.$(OBJEXT) diff --git a/enum.c b/enum.c index 7f15836ba87e77..cd48e80866671b 100644 --- a/enum.c +++ b/enum.c @@ -4650,7 +4650,7 @@ sum_iter(VALUE i, struct enum_sum_memo *memo) } else switch (TYPE(memo->v)) { default: sum_iter_some_value(i, memo); return; - case T_FLOAT: sum_iter_Kahan_Babuska(i, memo); return; + case T_FLOAT: case T_FIXNUM: case T_BIGNUM: case T_RATIONAL: diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c index 866a49eff4590e..1812ba1d08c601 100644 --- a/ext/objspace/objspace_dump.c +++ b/ext/objspace/objspace_dump.c @@ -547,9 +547,8 @@ dump_object(VALUE obj, struct dump_config *dc) if (dc->cur_obj_klass) { VALUE mod_name = rb_mod_name(obj); if (!NIL_P(mod_name)) { - dump_append(dc, ", \"name\":\""); - dump_append(dc, RSTRING_PTR(mod_name)); - dump_append(dc, "\""); + dump_append(dc, ", \"name\":"); + dump_append_string_value(dc, mod_name); } else { VALUE real_mod_name = rb_mod_name(rb_class_real(obj)); diff --git a/hash.c b/hash.c index 902a2373b39a6b..79e869b13f7d4a 100644 --- a/hash.c +++ b/hash.c @@ -5931,24 +5931,23 @@ env_to_s(VALUE _) static VALUE env_inspect(VALUE _) { - VALUE i; VALUE str = rb_str_buf_new2("{"); + rb_encoding *enc = env_encoding(); ENV_LOCK(); { char **env = GET_ENVIRON(environ); while (*env) { - char *s = strchr(*env, '='); + const char *s = strchr(*env, '='); if (env != environ) { rb_str_buf_cat2(str, ", "); } if (s) { - rb_str_buf_cat2(str, "\""); - rb_str_buf_cat(str, *env, s-*env); - rb_str_buf_cat2(str, "\"=>"); - i = rb_inspect(rb_str_new2(s+1)); - rb_str_buf_append(str, i); + rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(*env, s-*env, enc))); + rb_str_buf_cat2(str, "=>"); + s++; + rb_str_buf_append(str, rb_str_inspect(env_enc_str_new(s, strlen(s), enc))); } env++; } diff --git a/internal/proc.h b/internal/proc.h index c75f15b283cb42..c6a9e38bfa25c8 100644 --- a/internal/proc.h +++ b/internal/proc.h @@ -23,6 +23,7 @@ VALUE rb_block_to_s(VALUE self, const struct rb_block *block, const char *additi VALUE rb_callable_receiver(VALUE); VALUE rb_func_proc_new(rb_block_call_func_t func, VALUE val); +VALUE rb_func_proc_dup(VALUE src_obj); VALUE rb_func_lambda_new(rb_block_call_func_t func, VALUE val, int min_argc, int max_argc); VALUE rb_iseq_location(const struct rb_iseq_struct *iseq); VALUE rb_sym_to_proc(VALUE sym); diff --git a/io.c b/io.c index 9d651e016a99fa..17f8d5799a68b3 100644 --- a/io.c +++ b/io.c @@ -3820,8 +3820,33 @@ rscheck(const char *rsptr, long rslen, VALUE rs) rb_raise(rb_eRuntimeError, "rs modified"); } +static const char * +search_delim(const char *p, long len, int delim, rb_encoding *enc) +{ + if (rb_enc_mbminlen(enc) == 1) { + p = memchr(p, delim, len); + if (p) return p + 1; + } + else { + const char *end = p + len; + while (p < end) { + int r = rb_enc_precise_mbclen(p, end, enc); + if (!MBCLEN_CHARFOUND_P(r)) { + p += rb_enc_mbminlen(enc); + continue; + } + int n = MBCLEN_CHARFOUND_LEN(r); + if (rb_enc_mbc_to_codepoint(p, end, enc) == (unsigned int)delim) { + return p + n; + } + p += n; + } + } + return NULL; +} + static int -appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp) +appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp, rb_encoding *enc) { VALUE str = *strp; long limit = *lp; @@ -3836,9 +3861,9 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp) p = READ_CHAR_PENDING_PTR(fptr); if (0 < limit && limit < searchlen) searchlen = (int)limit; - e = memchr(p, delim, searchlen); + e = search_delim(p, searchlen, delim, enc); if (e) { - int len = (int)(e-p+1); + int len = (int)(e-p); if (NIL_P(str)) *strp = str = rb_str_new(p, len); else @@ -3878,8 +3903,8 @@ appendline(rb_io_t *fptr, int delim, VALUE *strp, long *lp) long last; if (limit > 0 && pending > limit) pending = limit; - e = memchr(p, delim, pending); - if (e) pending = e - p + 1; + e = search_delim(p, pending, delim, enc); + if (e) pending = e - p; if (!NIL_P(str)) { last = RSTRING_LEN(str); rb_str_resize(str, last + pending); @@ -4139,16 +4164,26 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr) rsptr = RSTRING_PTR(rs); rslen = RSTRING_LEN(rs); } + newline = '\n'; + } + else if (rb_enc_mbminlen(enc) == 1) { + rsptr = RSTRING_PTR(rs); + newline = (unsigned char)rsptr[rslen - 1]; } else { + rs = rb_str_encode(rs, rb_enc_from_encoding(enc), 0, Qnil); rsptr = RSTRING_PTR(rs); + const char *e = rsptr + rslen; + const char *last = rb_enc_prev_char(rsptr, e, e, enc); + int n; + newline = rb_enc_codepoint_len(last, e, &n, enc); + if (last + n != e) rb_raise(rb_eArgError, "broken separator"); } - newline = (unsigned char)rsptr[rslen - 1]; - chomp_cr = chomp && rslen == 1 && newline == '\n'; + chomp_cr = chomp && newline == '\n' && rslen == rb_enc_mbminlen(enc); } /* MS - Optimization */ - while ((c = appendline(fptr, newline, &str, &limit)) != EOF) { + while ((c = appendline(fptr, newline, &str, &limit, enc)) != EOF) { const char *s, *p, *pp, *e; if (c == newline) { @@ -4169,8 +4204,8 @@ rb_io_getline_0(VALUE rs, long limit, int chomp, rb_io_t *fptr) if (limit == 0) { s = RSTRING_PTR(str); p = RSTRING_END(str); - pp = rb_enc_left_char_head(s, p-1, p, enc); - if (extra_limit && + pp = rb_enc_prev_char(s, p, p, enc); + if (extra_limit && pp && MBCLEN_NEEDMORE_P(rb_enc_precise_mbclen(pp, p, enc))) { /* relax the limit while incomplete character. * extra_limit limits the relax length */ @@ -4372,23 +4407,31 @@ rb_io_set_lineno(VALUE io, VALUE lineno) static VALUE io_readline(rb_execution_context_t *ec, VALUE io, VALUE sep, VALUE lim, VALUE chomp) { + long limit = -1; if (NIL_P(lim)) { + VALUE tmp = Qnil; // If sep is specified, but it's not a string and not nil, then assume // it's the limit (it should be an integer) - if (!NIL_P(sep) && NIL_P(rb_check_string_type(sep))) { + if (!NIL_P(sep) && NIL_P(tmp = rb_check_string_type(sep))) { // If the user has specified a non-nil / non-string value // for the separator, we assume it's the limit and set the // separator to default: rb_rs. lim = sep; + limit = NUM2LONG(lim); sep = rb_rs; } + else { + sep = tmp; + } } - - if (!NIL_P(sep)) { - StringValue(sep); + else { + if (!NIL_P(sep)) StringValue(sep); + limit = NUM2LONG(lim); } - VALUE line = rb_io_getline_1(sep, NIL_P(lim) ? -1L : NUM2LONG(lim), RTEST(chomp), io); + check_getline_args(&sep, &limit, io); + + VALUE line = rb_io_getline_1(sep, limit, RTEST(chomp), io); rb_lastline_set_up(line, 1); if (NIL_P(line)) { @@ -7959,7 +8002,7 @@ popen_finish(VALUE port, VALUE klass) if (NIL_P(port)) { /* child */ if (rb_block_given_p()) { - rb_yield(Qnil); + rb_protect(rb_yield, Qnil, NULL); rb_io_flush(rb_ractor_stdout()); rb_io_flush(rb_ractor_stderr()); _exit(0); diff --git a/iseq.c b/iseq.c index 27c5bb5d82d907..672bbccd987bb8 100644 --- a/iseq.c +++ b/iseq.c @@ -113,7 +113,9 @@ remove_from_constant_cache(ID id, IC ic) st_table *ics = (st_table *)lookup_result; st_delete(ics, &ic_data, NULL); - if (ics->num_entries == 0) { + if (ics->num_entries == 0 && + // See comment in vm_track_constant_cache on why we need this check + id != vm->inserting_constant_cache_id) { rb_id_table_delete(vm->constant_cache, id); st_free_table(ics); } diff --git a/iseq.h b/iseq.h index d71f37ca133e89..478f02afaf11ba 100644 --- a/iseq.h +++ b/iseq.h @@ -117,6 +117,7 @@ struct iseq_compile_data { struct iseq_compile_data_storage *storage_current; } insn; bool in_rescue; + bool in_masgn; int loopval_popped; /* used by NODE_BREAK */ int last_line; int label_no; diff --git a/main.c b/main.c index 072dc56dd54c11..97c2858eea8b4a 100644 --- a/main.c +++ b/main.c @@ -44,6 +44,12 @@ int rb_wasm_rt_start(int (main)(int argc, char **argv), int argc, char **argv); #define rb_main(argc, argv) rb_wasm_rt_start(rb_main, argc, argv) #endif +#ifdef _WIN32 +#define main(argc, argv) w32_main(argc, argv) +static int main(int argc, char **argv); +int wmain(void) {return main(0, NULL);} +#endif + int main(int argc, char **argv) { diff --git a/proc.c b/proc.c index 9405a1a09fff10..a3fdb1783ce755 100644 --- a/proc.c +++ b/proc.c @@ -680,6 +680,29 @@ cfunc_proc_new(VALUE klass, VALUE ifunc) return procval; } +VALUE +rb_func_proc_dup(VALUE src_obj) +{ + RUBY_ASSERT(rb_typeddata_is_instance_of(src_obj, &proc_data_type)); + + rb_proc_t *src_proc; + GetProcPtr(src_obj, src_proc); + RUBY_ASSERT(vm_block_type(&src_proc->block) == block_type_ifunc); + + cfunc_proc_t *proc; + VALUE proc_obj = TypedData_Make_Struct(rb_obj_class(src_obj), cfunc_proc_t, &proc_data_type, proc); + + memcpy(&proc->basic, src_proc, sizeof(rb_proc_t)); + + VALUE *ep = *(VALUE **)&proc->basic.block.as.captured.ep = proc->env + VM_ENV_DATA_SIZE - 1; + ep[VM_ENV_DATA_INDEX_FLAGS] = src_proc->block.as.captured.ep[VM_ENV_DATA_INDEX_FLAGS]; + ep[VM_ENV_DATA_INDEX_ME_CREF] = src_proc->block.as.captured.ep[VM_ENV_DATA_INDEX_ME_CREF]; + ep[VM_ENV_DATA_INDEX_SPECVAL] = src_proc->block.as.captured.ep[VM_ENV_DATA_INDEX_SPECVAL]; + ep[VM_ENV_DATA_INDEX_ENV] = src_proc->block.as.captured.ep[VM_ENV_DATA_INDEX_ENV]; + + return proc_obj; +} + static VALUE sym_proc_new(VALUE klass, VALUE sym) { @@ -1300,12 +1323,17 @@ proc_eq(VALUE self, VALUE other) } break; case block_type_ifunc: - if (self_block->as.captured.ep != \ - other_block->as.captured.ep || - self_block->as.captured.code.ifunc != \ + if (self_block->as.captured.code.ifunc != \ other_block->as.captured.code.ifunc) { return Qfalse; } + + if (memcmp( + ((cfunc_proc_t *)self_proc)->env, + ((cfunc_proc_t *)other_proc)->env, + sizeof(((cfunc_proc_t *)self_proc)->env))) { + return Qfalse; + } break; case block_type_proc: if (self_block->as.proc != other_block->as.proc) { @@ -1434,6 +1462,7 @@ rb_hash_proc(st_index_t hash, VALUE prc) break; case block_type_ifunc: hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.ifunc->func); + hash = rb_st_hash_uint(hash, (st_index_t)proc->block.as.captured.code.ifunc->data); break; case block_type_symbol: hash = rb_st_hash_uint(hash, rb_any_hash(proc->block.as.symbol)); @@ -1445,7 +1474,14 @@ rb_hash_proc(st_index_t hash, VALUE prc) rb_bug("rb_hash_proc: unknown block type %d", vm_block_type(&proc->block)); } - return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep); + /* ifunc procs have their own allocated ep. If an ifunc is duplicated, they + * will point to different ep but they should return the same hash code, so + * we cannot include the ep in the hash. */ + if (vm_block_type(&proc->block) != block_type_ifunc) { + hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep); + } + + return hash; } diff --git a/regexec.c b/regexec.c index bae338cb789cf0..22c1ad050df9c1 100644 --- a/regexec.c +++ b/regexec.c @@ -4217,9 +4217,8 @@ match_at(regex_t* reg, const UChar* str, const UChar* end, return ONIGERR_UNEXPECTED_BYTECODE; timeout: + STACK_SAVE; xfree(xmalloc_base); - if (stk_base != stk_alloc || IS_NOT_NULL(msa->stack_p)) - xfree(stk_base); return ONIGERR_TIMEOUT; } diff --git a/scheduler.c b/scheduler.c index 3159635dba5804..0906bc0101594e 100644 --- a/scheduler.c +++ b/scheduler.c @@ -403,7 +403,15 @@ rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber) { VM_ASSERT(rb_obj_is_fiber(fiber)); - return rb_funcall(scheduler, id_unblock, 2, blocker, fiber); + // `rb_fiber_scheduler_unblock` can be called from points where `errno` is expected to be preserved. Therefore, we should save and restore it. For example `io_binwrite` calls `rb_fiber_scheduler_unblock` and if `errno` is reset to 0 by user code, it will break the error handling in `io_write`. + // If we explicitly preserve `errno` in `io_binwrite` and other similar functions (e.g. by returning it), this code is no longer needed. I hope in the future we will be able to remove it. + int saved_errno = errno; + + VALUE result = rb_funcall(scheduler, id_unblock, 2, blocker, fiber); + + errno = saved_errno; + + return result; } /* diff --git a/sprintf.c b/sprintf.c index b2d89617aaf64b..b13530614fd709 100644 --- a/sprintf.c +++ b/sprintf.c @@ -247,8 +247,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) } #define update_coderange(partial) do { \ - if (coderange != ENC_CODERANGE_BROKEN && scanned < blen \ - && rb_enc_to_index(enc) /* != ENCINDEX_ASCII_8BIT */) { \ + if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) { \ int cr = coderange; \ scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &cr); \ ENC_CODERANGE_SET(result, \ @@ -812,7 +811,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) if (FIXNUM_P(num)) { if ((SIGNED_VALUE)num < 0) { long n = -FIX2LONG(num); - num = LONG2FIX(n); + num = LONG2NUM(n); sign = -1; } } diff --git a/string.c b/string.c index 635be6988d2149..a28d682a841a60 100644 --- a/string.c +++ b/string.c @@ -2891,11 +2891,12 @@ rb_str_subpos(VALUE str, long beg, long *lenp) { long len = *lenp; long slen = -1L; - long blen = RSTRING_LEN(str); + const long blen = RSTRING_LEN(str); rb_encoding *enc = STR_ENC_GET(str); char *p, *s = RSTRING_PTR(str), *e = s + blen; if (len < 0) return 0; + if (beg < 0 && -beg < 0) return 0; if (!blen) { len = 0; } @@ -2913,7 +2914,8 @@ rb_str_subpos(VALUE str, long beg, long *lenp) } if (beg < 0) { if (len > -beg) len = -beg; - if (-beg * rb_enc_mbmaxlen(enc) < RSTRING_LEN(str) / 8) { + if ((ENC_CODERANGE(str) == ENC_CODERANGE_VALID) && + (-beg * rb_enc_mbmaxlen(enc) < blen / 8)) { beg = -beg; while (beg-- > len && (e = rb_enc_prev_char(s, e, e, enc)) != 0); p = e; @@ -2931,7 +2933,7 @@ rb_str_subpos(VALUE str, long beg, long *lenp) if (len == 0) goto end; } } - else if (beg > 0 && beg > RSTRING_LEN(str)) { + else if (beg > 0 && beg > blen) { return 0; } if (len == 0) { diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb index 34effad816316a..62424fc48939f6 100644 --- a/test/fiber/test_scheduler.rb +++ b/test/fiber/test_scheduler.rb @@ -182,4 +182,32 @@ def test_deadlock thread.join signaller.join end + + def test_condition_variable + condition_variable = ::Thread::ConditionVariable.new + mutex = ::Thread::Mutex.new + + error = nil + + thread = Thread.new do + Thread.current.report_on_exception = false + + scheduler = Scheduler.new + Fiber.set_scheduler scheduler + + fiber = Fiber.schedule do + begin + mutex.synchronize do + condition_variable.wait(mutex) + end + rescue => error + end + end + + fiber.raise(RuntimeError) + end + + thread.join + assert_kind_of RuntimeError, error + end end diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb index 86f6c00bbbc7f5..c0d66ba3542c09 100644 --- a/test/net/http/test_https.rb +++ b/test/net/http/test_https.rb @@ -167,19 +167,16 @@ def test_session_reuse def test_session_reuse_but_expire # FIXME: The new_session_cb is known broken for clients in OpenSSL 1.1.0h. omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 1.1.0h') - omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 3.2.') - omit if OpenSSL::OPENSSL_LIBRARY_VERSION.include?('OpenSSL 3.3.') - omit "not working on MinGW" if /mingw/ =~ RUBY_PLATFORM http = Net::HTTP.new(HOST, config("port")) http.use_ssl = true http.cert_store = TEST_STORE - http.ssl_timeout = -1 + http.ssl_timeout = 1 http.start http.get("/") http.finish - + sleep 1.25 http.start http.get("/") diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 4e09fe1af7e7d7..7a1d4465c7fa66 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -900,6 +900,12 @@ def test_load_allocation_path_load_from_binary # load_allocation_path_helper 'iseq = RubyVM::InstructionSequence.load_from_binary(File.binread(path))', to_binary: true end + def test_escape_class_name + class_name = '" little boby table [Bug #20892]' + json = ObjectSpace.dump(Class.new.tap { |c| c.set_temporary_name(class_name) }) + assert_equal class_name, JSON.parse(json)["name"] + end + def test_utf8_method_names name = "utf8_❨╯°□°❩╯︵┻━┻" obj = ObjectSpace.trace_object_allocations do diff --git a/test/openssl/test_x509req.rb b/test/openssl/test_x509req.rb index 8525cf4b8fadd6..b98754b8c8e46b 100644 --- a/test/openssl/test_x509req.rb +++ b/test/openssl/test_x509req.rb @@ -35,16 +35,10 @@ def test_public_key end def test_version - omit "not working on MinGW" if /mingw/ =~ RUBY_PLATFORM req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest.new('SHA256')) assert_equal(0, req.version) req = OpenSSL::X509::Request.new(req.to_der) assert_equal(0, req.version) - - req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest.new('SHA256')) - assert_equal(1, req.version) - req = OpenSSL::X509::Request.new(req.to_der) - assert_equal(1, req.version) end def test_subject @@ -107,7 +101,7 @@ def test_sign_and_verify_rsa_sha1 assert_equal(false, req.verify(@rsa2048)) assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) - req.version = 1 + req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBarFooBar") assert_equal(false, req.verify(@rsa1024)) rescue OpenSSL::X509::RequestError # RHEL 9 disables SHA1 end diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb index 903cd62b0263c2..12f7d6485a1193 100644 --- a/test/ruby/test_argf.rb +++ b/test/ruby/test_argf.rb @@ -267,7 +267,6 @@ def test_inplace_rename_impossible end def test_inplace_nonascii - omit "not working on MinGW" if /mingw/ =~ RUBY_PLATFORM ext = Encoding.default_external or omit "no default external encoding" t = nil diff --git a/test/ruby/test_assignment.rb b/test/ruby/test_assignment.rb index 3a8dafb7f0af00..3d0e773c825664 100644 --- a/test/ruby/test_assignment.rb +++ b/test/ruby/test_assignment.rb @@ -248,6 +248,16 @@ def test_massign_splat a,b,*c = *[*[1,2]]; assert_equal([1,2,[]], [a,b,c]) end + def test_massign_optimized_literal_bug_21012 + a = [] + def a.[]=(*args) + push args + end + a["a", "b"], = 1 + a["a", 10], = 2 + assert_equal [["a", "b", 1], ["a", 10, 2]], a + end + def test_assign_rescue a = raise rescue 2; assert_equal(2, a) a, b = raise rescue [3,4]; assert_equal([3, 4], [a, b]) diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb index 825c191d874c81..ec24740d753f88 100644 --- a/test/ruby/test_enumerator.rb +++ b/test/ruby/test_enumerator.rb @@ -1044,4 +1044,19 @@ def test_freeze assert_raise(FrozenError) { e.feed 1 } assert_raise(FrozenError) { e.rewind } end + + def test_sum_of_numeric + num = Class.new(Numeric) do + attr_reader :to_f + def initialize(val) + @to_f = Float(val) + end + end + + ary = [5, 10, 20].map {|i| num.new(i)} + + assert_equal(35.0, ary.sum) + enum = ary.each + assert_equal(35.0, enum.sum) + end end diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb index cdadeac14886e8..466d8d9d122c25 100644 --- a/test/ruby/test_env.rb +++ b/test/ruby/test_env.rb @@ -353,6 +353,13 @@ def test_inspect end end + def test_inspect_encoding + ENV.clear + key = "VAR\u{e5 e1 e2 e4 e3 101 3042}" + ENV[key] = "foo" + assert_equal(%{{"VAR\u{e5 e1 e2 e4 e3 101 3042}"=>"foo"}}, ENV.inspect) + end + def test_to_a ENV.clear ENV["foo"] = "bar" diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 476d9f882fc579..51c9f2b83c5018 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -2002,6 +2002,44 @@ def test_readline_chomp_true end end + def test_readline_incompatible_rs + first_line = File.open(__FILE__, &:gets).encode("utf-32le") + File.open(__FILE__, encoding: "utf-8:utf-32le") {|f| + assert_equal first_line, f.readline + assert_raise(ArgumentError) {f.readline("\0")} + } + end + + def test_readline_limit_nonascii + mkcdtmpdir do + i = 0 + + File.open("text#{i+=1}", "w+:utf-8") do |f| + f.write("Test\nok\u{bf}ok\n") + f.rewind + + assert_equal("Test\nok\u{bf}", f.readline("\u{bf}")) + assert_equal("ok\n", f.readline("\u{bf}")) + end + + File.open("text#{i+=1}", "w+b:utf-32le") do |f| + f.write("0123456789") + f.rewind + + assert_equal(4, f.readline(4).bytesize) + assert_equal(4, f.readline(3).bytesize) + end + + File.open("text#{i+=1}", "w+:utf-8:utf-32le") do |f| + f.write("0123456789") + f.rewind + + assert_equal(4, f.readline(4).bytesize) + assert_equal(4, f.readline(3).bytesize) + end + end + end + def test_set_lineno_readline pipe(proc do |w| w.puts "foo" diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 423ebd391a904e..1a3e6fb5b7fa94 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -209,6 +209,27 @@ def o.foo; end assert_kind_of(String, o.method(:foo).hash.to_s) end + def test_hash_does_not_change_after_compaction + omit "compaction is not supported on this platform" unless GC.respond_to?(:compact) + + # iseq backed method + assert_separately([], <<~RUBY) + def a; end + + # Need this method here because otherwise the iseq may be on the C stack + # which would get pinned and not move during compaction + def get_hash + method(:a).hash + end + + hash = get_hash + + GC.verify_compaction_references(expand_heap: true, toward: :empty) + + assert_equal(hash, get_hash) + RUBY + end + def test_owner c = Class.new do def foo; end diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb index 4722fa22e0b745..29b71bc027e700 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -784,6 +784,18 @@ def test_prepend_after_include assert_equal([:m1, :m0, :m, :sc, :m1, :m0, :c], sc.new.m) end + def test_include_into_module_after_prepend_bug_20871 + bar = Module.new{def bar; 'bar'; end} + foo = Module.new{def foo; 'foo'; end} + m = Module.new + c = Class.new{include m} + m.prepend bar + Class.new{include m} + m.include foo + assert_include c.ancestors, foo + assert_equal "foo", c.new.foo + end + def test_protected_include_into_included_module m1 = Module.new do def other_foo(other) diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 19fc89357aba8c..511826c636c166 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -410,6 +410,18 @@ def test_dup_subclass assert_throw(:initialize_dup) {c1.new{}.dup} end + def test_dup_ifunc_proc_bug_20950 + assert_normal_exit(<<~RUBY, "[Bug #20950]") + p = { a: 1 }.to_proc + 100.times do + p = p.dup + GC.start + p.call + rescue ArgumentError + end + RUBY + end + def test_clone_subclass c1 = Class.new(Proc) assert_equal c1, c1.new{}.clone.class, '[Bug #17545]' diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index d9804aaa57342c..ffcca99eb3661d 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -923,15 +923,29 @@ def test_execopts_popen_extra_fd } end - def test_popen_fork - IO.popen("-") {|io| - if !io - puts "fooo" - else - assert_equal("fooo\n", io.read) + if Process.respond_to?(:fork) + def test_popen_fork + IO.popen("-") do |io| + if !io + puts "fooo" + else + assert_equal("fooo\n", io.read) + end end - } - rescue NotImplementedError + end + + def test_popen_fork_ensure + IO.popen("-") do |io| + if !io + STDERR.reopen(STDOUT) + raise "fooo" + else + assert_empty io.read + end + end + rescue RuntimeError + abort "[Bug #20995] should not reach here" + end end def test_fd_inheritance diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb index 6b9efcb555d2e6..010be019606223 100644 --- a/test/ruby/test_regexp.rb +++ b/test/ruby/test_regexp.rb @@ -1838,6 +1838,13 @@ def test_bug_20453 end; end + def test_bug_20886 + re = Regexp.new("d()*+|a*a*bc", timeout: 0.02) + assert_raise(Regexp::TimeoutError) do + re === "b" + "a" * 1000 + end + end + def per_instance_redos_test(global_timeout, per_instance_timeout, expected_timeout) assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") global_timeout = #{ EnvUtil.apply_timeout_scale(global_timeout).inspect } diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 1f71c8a444fca7..b256a70ba33653 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -347,7 +347,6 @@ def test_autosplit end def test_chdir - omit "not working on MinGW" if /mingw/ =~ RUBY_PLATFORM assert_in_out_err(%w(-C), "", [], /Can't chdir/) assert_in_out_err(%w(-C test_ruby_test_rubyoptions_foobarbazqux), "", [], /Can't chdir/) @@ -1044,7 +1043,6 @@ def test_command_line_glob_nonascii end def test_command_line_progname_nonascii - omit "not working on MinGW" if /mingw/ =~ RUBY_PLATFORM bug10555 = '[ruby-dev:48752] [Bug #10555]' name = expected = nil unless (0x80..0x10000).any? {|c| @@ -1096,7 +1094,6 @@ def assert_e_script_encoding(str, args = []) # Since the codepage is shared all processes per conhost.exe, do # not chcp, or parallel test may break. def test_locale_codepage - omit "not working on MinGW" if /mingw/ =~ RUBY_PLATFORM locale = Encoding.find("locale") list = %W"\u{c7} \u{452} \u{3066 3059 3068}" list.each do |s| diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index a90c8852473411..bf040681a1d345 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -93,6 +93,22 @@ def test_c_call_removed_method assert_equal([[:req]], parameters) end + def test_c_call_aliased_method + # [Bug #20915] + klass = Class.new do + alias_method :new_method, :method + end + + instance = klass.new + parameters = nil + + TracePoint.new(:c_call) do |tp| + parameters = tp.parameters + end.enable { instance.new_method(:to_s) } + + assert_equal([[:req]], parameters) + end + def test_call events = [] name = "#{self.class}\##{__method__}" diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb index c453ecd350b9b4..3f821f7a5dcc82 100644 --- a/test/ruby/test_sprintf.rb +++ b/test/ruby/test_sprintf.rb @@ -227,6 +227,10 @@ def test_rational bug11766 = '[ruby-core:71806] [Bug #11766]' assert_equal("x"*10+" 1.0", sprintf("x"*10+"%8.1f", 1r), bug11766) + + require 'rbconfig/sizeof' + fmin, fmax = RbConfig::LIMITS.values_at("FIXNUM_MIN", "FIXNUM_MAX") + assert_match(/\A-\d+\.\d+\z/, sprintf("%f", Rational(fmin, fmax))) end def test_rational_precision @@ -543,4 +547,12 @@ def test_negative_width_overflow sprintf("%*s", RbConfig::LIMITS["INT_MIN"], "") end end + + def test_binary_format_coderange + 1.upto(500) do |i| + str = sprintf("%*s".b, i, "\xe2".b) + refute_predicate str, :ascii_only? + assert_equal i, str.bytesize + end + end end diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index dcb81cfc6d6a33..a916781fe80f85 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -169,6 +169,15 @@ def o.to_int; 2; end assert_raise(ArgumentError) { "foo"[] } end + def test_AREF_underflow + require "rbconfig/sizeof" + assert_equal(nil, S("\u{3042 3044 3046}")[RbConfig::LIMITS["LONG_MIN"], 1]) + end + + def test_AREF_invalid_encoding + assert_equal(S("\x80"), S("A"*39+"\x80")[-1, 1]) + end + def test_ASET # '[]=' s = S("FooBar") s[0] = S('A') diff --git a/thread_sync.c b/thread_sync.c index c0a0ca7103369e..6b1d9939de2b3b 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -548,49 +548,55 @@ rb_mutex_abandon_all(rb_mutex_t *mutexes) } #endif -static VALUE -rb_mutex_sleep_forever(VALUE self) -{ - rb_thread_sleep_deadly_allow_spurious_wakeup(self, Qnil, 0); - return Qnil; -} +struct rb_mutex_sleep_arguments { + VALUE self; + VALUE timeout; +}; static VALUE -rb_mutex_wait_for(VALUE time) -{ - rb_hrtime_t *rel = (rb_hrtime_t *)time; - /* permit spurious check */ - return RBOOL(sleep_hrtime(GET_THREAD(), *rel, 0)); -} - -VALUE -rb_mutex_sleep(VALUE self, VALUE timeout) +mutex_sleep_begin(VALUE _arguments) { - struct timeval t; + struct rb_mutex_sleep_arguments *arguments = (struct rb_mutex_sleep_arguments *)_arguments; + VALUE timeout = arguments->timeout; VALUE woken = Qtrue; - if (!NIL_P(timeout)) { - t = rb_time_interval(timeout); - } - - rb_mutex_unlock(self); - time_t beg = time(0); - VALUE scheduler = rb_fiber_scheduler_current(); if (scheduler != Qnil) { rb_fiber_scheduler_kernel_sleep(scheduler, timeout); - mutex_lock_uninterruptible(self); } else { if (NIL_P(timeout)) { - rb_ensure(rb_mutex_sleep_forever, self, mutex_lock_uninterruptible, self); + rb_thread_sleep_deadly_allow_spurious_wakeup(arguments->self, Qnil, 0); } else { - rb_hrtime_t rel = rb_timeval2hrtime(&t); - woken = rb_ensure(rb_mutex_wait_for, (VALUE)&rel, mutex_lock_uninterruptible, self); + struct timeval timeout_value = rb_time_interval(timeout); + rb_hrtime_t relative_timeout = rb_timeval2hrtime(&timeout_value); + /* permit spurious check */ + woken = RBOOL(sleep_hrtime(GET_THREAD(), relative_timeout, 0)); } } + return woken; +} + +VALUE +rb_mutex_sleep(VALUE self, VALUE timeout) +{ + if (!NIL_P(timeout)) { + // Validate the argument: + rb_time_interval(timeout); + } + + rb_mutex_unlock(self); + time_t beg = time(0); + + struct rb_mutex_sleep_arguments arguments = { + .self = self, + .timeout = timeout, + }; + + VALUE woken = rb_ensure(mutex_sleep_begin, (VALUE)&arguments, mutex_lock_uninterruptible, self); + RUBY_VM_CHECK_INTS_BLOCKING(GET_EC()); if (!woken) return Qnil; time_t end = time(0) - beg; diff --git a/tool/bundler/test_gems.rb b/tool/bundler/test_gems.rb index bb7d4edb9a2bd9..2bf3a2ad65a73e 100644 --- a/tool/bundler/test_gems.rb +++ b/tool/bundler/test_gems.rb @@ -8,7 +8,7 @@ gem "webrick", "~> 1.8" gem "rack-test", "~> 2.1" gem "compact_index", "~> 0.15.0" -gem "sinatra", "~> 4.0" +gem "sinatra", "< 4.1" gem "rake", "~> 13.1" gem "builder", "~> 3.2" gem "rb_sys" diff --git a/version.h b/version.h index f399d3cb01ec07..cddd5935ff57cc 100644 --- a/version.h +++ b/version.h @@ -9,9 +9,9 @@ */ # define RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR -#define RUBY_VERSION_TEENY 6 +#define RUBY_VERSION_TEENY 7 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 108 +#define RUBY_PATCHLEVEL 123 #include "ruby/version.h" #include "ruby/internal/abi.h" diff --git a/vm.c b/vm.c index 665ffcbdeda844..a0fe0104b0dc52 100644 --- a/vm.c +++ b/vm.c @@ -1158,7 +1158,16 @@ rb_proc_dup(VALUE self) rb_proc_t *src; GetProcPtr(self, src); - procval = proc_create(rb_obj_class(self), &src->block, src->is_from_method, src->is_lambda); + + switch (vm_block_type(&src->block)) { + case block_type_ifunc: + procval = rb_func_proc_dup(self); + break; + default: + procval = proc_create(rb_obj_class(self), &src->block, src->is_from_method, src->is_lambda); + break; + } + if (RB_OBJ_SHAREABLE_P(self)) FL_SET_RAW(procval, RUBY_FL_SHAREABLE); RB_GC_GUARD(self); /* for: body = rb_proc_dup(body) */ return procval; @@ -3495,7 +3504,7 @@ thread_alloc(VALUE klass) return TypedData_Make_Struct(klass, rb_thread_t, &thread_data_type, th); } -inline void +void rb_ec_set_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t size) { ec->vm_stack = stack; diff --git a/vm_core.h b/vm_core.h index e192ae23250172..30bfb8404acfb2 100644 --- a/vm_core.h +++ b/vm_core.h @@ -759,6 +759,7 @@ typedef struct rb_vm_struct { // and Qtrue as values. It is used when inline constant caches need to be // invalidated or ISEQs are being freed. struct rb_id_table *constant_cache; + ID inserting_constant_cache_id; #ifndef VM_GLOBAL_CC_CACHE_TABLE_SIZE #define VM_GLOBAL_CC_CACHE_TABLE_SIZE 1023 diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 9a8911f731badf..7284769854fcbf 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -5809,7 +5809,8 @@ rb_vm_opt_newarray_hash(rb_execution_context_t *ec, rb_num_t num, const VALUE *p static void vm_track_constant_cache(ID id, void *ic) { - struct rb_id_table *const_cache = GET_VM()->constant_cache; + rb_vm_t *vm = GET_VM(); + struct rb_id_table *const_cache = vm->constant_cache; VALUE lookup_result; st_table *ics; @@ -5821,7 +5822,23 @@ vm_track_constant_cache(ID id, void *ic) rb_id_table_insert(const_cache, id, (VALUE)ics); } + /* The call below to st_insert could allocate which could trigger a GC. + * If it triggers a GC, it may free an iseq that also holds a cache to this + * constant. If that iseq is the last iseq with a cache to this constant, then + * it will free this ST table, which would cause an use-after-free during this + * st_insert. + * + * So to fix this issue, we store the ID that is currently being inserted + * and, in remove_from_constant_cache, we don't free the ST table for ID + * equal to this one. + * + * See [Bug #20921]. + */ + vm->inserting_constant_cache_id = id; + st_insert(ics, (st_data_t) ic, (st_data_t) Qtrue); + + vm->inserting_constant_cache_id = (ID)0; } static void diff --git a/vm_method.c b/vm_method.c index 232ba03e615d6b..7b57b56cd634c9 100644 --- a/vm_method.c +++ b/vm_method.c @@ -2237,7 +2237,7 @@ rb_hash_method_definition(st_index_t hash, const rb_method_definition_t *def) switch (def->type) { case VM_METHOD_TYPE_ISEQ: - return rb_hash_uint(hash, (st_index_t)def->body.iseq.iseqptr); + return rb_hash_uint(hash, (st_index_t)def->body.iseq.iseqptr->body); case VM_METHOD_TYPE_CFUNC: hash = rb_hash_uint(hash, (st_index_t)def->body.cfunc.func); return rb_hash_uint(hash, def->body.cfunc.argc); diff --git a/vm_trace.c b/vm_trace.c index c2762b73f2d7b8..7050d1efc2eb45 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -937,6 +937,9 @@ rb_tracearg_parameters(rb_trace_arg_t *trace_arg) const rb_method_entry_t *me; VALUE iclass = Qnil; me = rb_method_entry_without_refinements(trace_arg->klass, trace_arg->called_id, &iclass); + if (!me) { + me = rb_method_entry_without_refinements(trace_arg->klass, trace_arg->id, &iclass); + } return rb_unnamed_parameters(rb_method_entry_arity(me)); } break; diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 93bc1e64588e28..21dbd05812ae53 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -158,6 +158,9 @@ OPTFLAGS = -O2b2xg- OPTFLAGS = -O2sy- !endif !endif +!if $(MSC_VER) >= 1900 +OPTFLAGS = $(OPTFLAGS) -Zc:inline +!endif !if !defined(incflags) incflags = !endif diff --git a/win32/winmain.c b/win32/winmain.c index 467a835d290fda..a9f38099dba5d3 100644 --- a/win32/winmain.c +++ b/win32/winmain.c @@ -1,10 +1,10 @@ #include #include -extern int main(int, char**); +extern int wmain(int, WCHAR**); int WINAPI WinMain(HINSTANCE current, HINSTANCE prev, LPSTR cmdline, int showcmd) { - return main(0, NULL); + return wmain(0, NULL); } diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index c672a40d1e1752..d702b1133e1847 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -5434,8 +5434,12 @@ fn gen_send_cfunc( // Increment total cfunc send count gen_counter_incr(asm, Counter::num_send_cfunc); - // Delegate to codegen for C methods if we have it. - if kw_arg.is_null() && flags & VM_CALL_OPT_SEND == 0 && flags & VM_CALL_ARGS_SPLAT == 0 && (cfunc_argc == -1 || argc == cfunc_argc) { + // Delegate to codegen for C methods if we have it and the callsite is simple enough. + if kw_arg.is_null() && + flags & VM_CALL_OPT_SEND == 0 && + flags & VM_CALL_ARGS_SPLAT == 0 && + flags & VM_CALL_ARGS_BLOCKARG == 0 && + (cfunc_argc == -1 || argc == cfunc_argc) { let codegen_p = lookup_cfunc_codegen(unsafe { (*cme).def }); let expected_stack_after = asm.ctx.get_stack_size() as i32 - argc; if let Some(known_cfunc_codegen) = codegen_p {