diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 8aac52ef47e546..6c178c12f2b4f4 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -147,7 +147,7 @@ def find_openssl_library
have_func("EVP_PKEY_check(NULL)", evp_h)
# added in 3.0.0
-have_func("SSL_set0_tmp_dh_pkey(NULL, NULL)", ssl_h)
+have_func("SSL_CTX_set0_tmp_dh_pkey(NULL, NULL)", ssl_h)
have_func("ERR_get_error_all(NULL, NULL, NULL, NULL, NULL)", "openssl/err.h")
have_func("SSL_CTX_load_verify_file(NULL, \"\")", ssl_h)
have_func("BN_check_prime(NULL, NULL, NULL)", "openssl/bn.h")
diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
index 46509c333e0857..3268c126b9efad 100644
--- a/ext/openssl/lib/openssl/ssl.rb
+++ b/ext/openssl/lib/openssl/ssl.rb
@@ -32,25 +32,6 @@ class SSLContext
}.call
}
- if defined?(OpenSSL::PKey::DH)
- DH_ffdhe2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
------BEGIN DH PARAMETERS-----
-MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
-+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
-87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
-YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
-7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
-ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
------END DH PARAMETERS-----
- _end_of_pem_
- private_constant :DH_ffdhe2048
-
- DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
- warn "using default DH parameters." if $VERBOSE
- DH_ffdhe2048
- }
- end
-
if !OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL")
DEFAULT_PARAMS.merge!(
min_version: OpenSSL::SSL::TLS1_VERSION,
@@ -85,26 +66,14 @@ class SSLContext
AES256-SHA256
AES128-SHA
AES256-SHA
- }.join(":"),
+ }.join(":").freeze,
)
end
+ DEFAULT_PARAMS.freeze
DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
DEFAULT_CERT_STORE.set_default_paths
- # A callback invoked when DH parameters are required for ephemeral DH key
- # exchange.
- #
- # The callback is invoked with the SSLSocket, a
- # flag indicating the use of an export cipher and the keylength
- # required.
- #
- # The callback must return an OpenSSL::PKey::DH instance of the correct
- # key length.
- #
- # Deprecated in version 3.0. Use #tmp_dh= instead.
- attr_accessor :tmp_dh_callback
-
# A callback invoked at connect time to distinguish between multiple
# server names.
#
@@ -146,7 +115,14 @@ def set_params(params={})
params.each{|name, value| self.__send__("#{name}=", value) }
if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
unless self.ca_file or self.ca_path or self.cert_store
- self.cert_store = DEFAULT_CERT_STORE
+ if not defined?(Ractor) or Ractor.current == Ractor.main
+ self.cert_store = DEFAULT_CERT_STORE
+ else
+ self.cert_store = Ractor.current[:__openssl_default_store__] ||=
+ OpenSSL::X509::Store.new.tap { |store|
+ store.set_default_paths
+ }
+ end
end
end
return params
@@ -456,10 +432,6 @@ def client_cert_cb
@context.client_cert_cb
end
- def tmp_dh_callback
- @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
- end
-
def session_new_cb
@context.session_new_cb
end
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 29564a8139ac2e..583396ebfc6f3b 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -36,8 +36,7 @@ VALUE cSSLSocket;
static VALUE eSSLErrorWaitReadable;
static VALUE eSSLErrorWaitWritable;
-static ID id_call, ID_callback_state, id_tmp_dh_callback,
- id_npn_protocols_encoded, id_each;
+static ID id_call, ID_callback_state, id_npn_protocols_encoded, id_each;
static VALUE sym_exception, sym_wait_readable, sym_wait_writable;
static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
@@ -47,7 +46,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb,
id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols,
id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb,
- id_i_verify_hostname, id_i_keylog_cb;
+ id_i_verify_hostname, id_i_keylog_cb, id_i_tmp_dh_callback;
static ID id_i_io, id_i_context, id_i_hostname;
static int ossl_ssl_ex_ptr_idx;
@@ -90,6 +89,7 @@ ossl_sslctx_s_alloc(VALUE klass)
ossl_raise(eSSLError, "SSL_CTX_new");
}
SSL_CTX_set_mode(ctx, mode);
+ SSL_CTX_set_dh_auto(ctx, 1);
RTYPEDDATA_DATA(obj) = ctx;
SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_ptr_idx, (void *)obj);
@@ -133,8 +133,6 @@ ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
#if !defined(OPENSSL_NO_DH)
struct tmp_dh_callback_args {
VALUE ssl_obj;
- ID id;
- int type;
int is_export;
int keylength;
};
@@ -143,48 +141,36 @@ static VALUE
ossl_call_tmp_dh_callback(VALUE arg)
{
struct tmp_dh_callback_args *args = (struct tmp_dh_callback_args *)arg;
- VALUE cb, dh;
- EVP_PKEY *pkey;
+ VALUE ctx_obj, cb, obj;
+ const DH *dh;
- cb = rb_funcall(args->ssl_obj, args->id, 0);
+ ctx_obj = rb_attr_get(args->ssl_obj, id_i_context);
+ cb = rb_attr_get(ctx_obj, id_i_tmp_dh_callback);
if (NIL_P(cb))
- return (VALUE)NULL;
- dh = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export),
- INT2NUM(args->keylength));
- pkey = GetPKeyPtr(dh);
- if (EVP_PKEY_base_id(pkey) != args->type)
- return (VALUE)NULL;
+ return (VALUE)NULL;
+
+ obj = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export),
+ INT2NUM(args->keylength));
+ // TODO: We should riase if obj is not DH
+ dh = EVP_PKEY_get0_DH(GetPKeyPtr(obj));
+ if (!dh)
+ ossl_clear_error();
- return (VALUE)pkey;
+ return (VALUE)dh;
}
-#endif
-#if !defined(OPENSSL_NO_DH)
static DH *
ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength)
{
- VALUE rb_ssl;
- EVP_PKEY *pkey;
- struct tmp_dh_callback_args args;
int state;
-
- rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
- args.ssl_obj = rb_ssl;
- args.id = id_tmp_dh_callback;
- args.is_export = is_export;
- args.keylength = keylength;
- args.type = EVP_PKEY_DH;
-
- pkey = (EVP_PKEY *)rb_protect(ossl_call_tmp_dh_callback,
- (VALUE)&args, &state);
+ VALUE rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+ struct tmp_dh_callback_args args = {rb_ssl, is_export, keylength};
+ VALUE ret = rb_protect(ossl_call_tmp_dh_callback, (VALUE)&args, &state);
if (state) {
rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state));
return NULL;
}
- if (!pkey)
- return NULL;
-
- return (DH *)EVP_PKEY_get0_DH(pkey);
+ return (DH *)ret;
}
#endif /* OPENSSL_NO_DH */
@@ -703,7 +689,10 @@ ossl_sslctx_setup(VALUE self)
GetSSLCTX(self, ctx);
#if !defined(OPENSSL_NO_DH)
- SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback);
+ if (!NIL_P(rb_attr_get(self, id_i_tmp_dh_callback))) {
+ SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback);
+ SSL_CTX_set_dh_auto(ctx, 0);
+ }
#endif
#if !defined(OPENSSL_IS_AWSLC) /* AWS-LC has no support for TLS 1.3 PHA. */
@@ -1148,7 +1137,7 @@ ossl_sslctx_set_client_sigalgs(VALUE self, VALUE v)
* contained in the key object, if any, are ignored. The server will always
* generate a new key pair for each handshake.
*
- * Added in version 3.0. See also the man page SSL_set0_tmp_dh_pkey(3).
+ * Added in version 3.0. See also the man page SSL_CTX_set0_tmp_dh_pkey(3).
*
* Example:
* ctx = OpenSSL::SSL::SSLContext.new
@@ -1169,7 +1158,7 @@ ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg)
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH)
rb_raise(eSSLError, "invalid pkey type %s (expected DH)",
OBJ_nid2sn(EVP_PKEY_base_id(pkey)));
-#ifdef HAVE_SSL_SET0_TMP_DH_PKEY
+#ifdef HAVE_SSL_CTX_SET0_TMP_DH_PKEY
if (!SSL_CTX_set0_tmp_dh_pkey(ctx, pkey))
ossl_raise(eSSLError, "SSL_CTX_set0_tmp_dh_pkey");
EVP_PKEY_up_ref(pkey);
@@ -1178,6 +1167,9 @@ ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg)
ossl_raise(eSSLError, "SSL_CTX_set_tmp_dh");
#endif
+ // Turn off the "auto" DH parameters set by ossl_sslctx_s_alloc()
+ SSL_CTX_set_dh_auto(ctx, 0);
+
return arg;
}
#endif
@@ -2865,6 +2857,23 @@ Init_ossl_ssl(void)
*/
rb_attr(cSSLContext, rb_intern_const("client_cert_cb"), 1, 1, Qfalse);
+#ifndef OPENSSL_NO_DH
+ /*
+ * A callback invoked when DH parameters are required for ephemeral DH key
+ * exchange.
+ *
+ * The callback is invoked with the SSLSocket, a
+ * flag indicating the use of an export cipher and the keylength
+ * required.
+ *
+ * The callback must return an OpenSSL::PKey::DH instance of the correct
+ * key length.
+ *
+ * Deprecated in version 3.0. Use #tmp_dh= instead.
+ */
+ rb_attr(cSSLContext, rb_intern_const("tmp_dh_callback"), 1, 1, Qfalse);
+#endif
+
/*
* Sets the context in which a session can be reused. This allows
* sessions for multiple applications to be distinguished, for example, by
@@ -3258,7 +3267,6 @@ Init_ossl_ssl(void)
sym_wait_readable = ID2SYM(rb_intern_const("wait_readable"));
sym_wait_writable = ID2SYM(rb_intern_const("wait_writable"));
- id_tmp_dh_callback = rb_intern_const("tmp_dh_callback");
id_npn_protocols_encoded = rb_intern_const("npn_protocols_encoded");
id_each = rb_intern_const("each");
@@ -3289,6 +3297,7 @@ Init_ossl_ssl(void)
DefIVarID(servername_cb);
DefIVarID(verify_hostname);
DefIVarID(keylog_cb);
+ DefIVarID(tmp_dh_callback);
DefIVarID(io);
DefIVarID(context);
diff --git a/gems/bundled_gems b/gems/bundled_gems
index 4b95fa525004f7..20f4379dbbb6d7 100644
--- a/gems/bundled_gems
+++ b/gems/bundled_gems
@@ -39,7 +39,7 @@ ostruct 0.6.3 https://github.com/ruby/ostruct
pstore 0.2.0 https://github.com/ruby/pstore
benchmark 0.4.1 https://github.com/ruby/benchmark
logger 1.7.0 https://github.com/ruby/logger
-rdoc 6.14.2 https://github.com/ruby/rdoc
+rdoc 6.15.0 https://github.com/ruby/rdoc ac2a6fbf62b584a8325a665a9e7b368388bc7df6
win32ole 1.9.2 https://github.com/ruby/win32ole
irb 1.15.2 https://github.com/ruby/irb d43c3d764ae439706aa1b26a3ec299cc45eaed5b
reline 0.6.2 https://github.com/ruby/reline
diff --git a/prism/config.yml b/prism/config.yml
index a8a8e70aace78e..ed5c9d8b9cde98 100644
--- a/prism/config.yml
+++ b/prism/config.yml
@@ -60,6 +60,7 @@ errors:
- CONDITIONAL_WHILE_PREDICATE
- CONSTANT_PATH_COLON_COLON_CONSTANT
- DEF_ENDLESS
+ - DEF_ENDLESS_PARAMETERS
- DEF_ENDLESS_SETTER
- DEF_NAME
- DEF_PARAMS_TERM
diff --git a/prism/prism.c b/prism/prism.c
index f85a69332ed95c..cc1896ee3486ed 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -14612,6 +14612,18 @@ update_parameter_state(pm_parser_t *parser, pm_token_t *token, pm_parameters_ord
return true;
}
+/**
+ * Ensures that after parsing a parameter, the next token is not `=`.
+ * Some parameters like `def(* = 1)` cannot become optional. When no parens
+ * are present like in `def * = 1`, this creates ambiguity with endless method definitions.
+ */
+static inline void
+refute_optional_parameter(pm_parser_t *parser) {
+ if (match1(parser, PM_TOKEN_EQUAL)) {
+ pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
+ }
+}
+
/**
* Parse a list of parameters on a method definition.
*/
@@ -14664,6 +14676,10 @@ parse_parameters(
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_BLOCK;
}
+ if (!uses_parentheses) {
+ refute_optional_parameter(parser);
+ }
+
pm_block_parameter_node_t *param = pm_block_parameter_node_create(parser, &name, &operator);
if (repeated) {
pm_node_flag_set_repeated_parameter((pm_node_t *)param);
@@ -14685,6 +14701,10 @@ parse_parameters(
bool succeeded = update_parameter_state(parser, &parser->current, &order);
parser_lex(parser);
+ if (!uses_parentheses) {
+ refute_optional_parameter(parser);
+ }
+
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_ALL;
pm_forwarding_parameter_node_t *param = pm_forwarding_parameter_node_create(parser, &parser->previous);
@@ -14866,6 +14886,10 @@ parse_parameters(
context_pop(parser);
pm_parameters_node_keywords_append(params, param);
+ if (!uses_parentheses) {
+ refute_optional_parameter(parser);
+ }
+
// If parsing the value of the parameter resulted in error recovery,
// then we can put a missing node in its place and stop parsing the
// parameters entirely now.
@@ -14897,6 +14921,10 @@ parse_parameters(
parser->current_scope->parameters |= PM_SCOPE_PARAMETERS_FORWARDING_POSITIONALS;
}
+ if (!uses_parentheses) {
+ refute_optional_parameter(parser);
+ }
+
pm_node_t *param = (pm_node_t *) pm_rest_parameter_node_create(parser, &operator, &name);
if (repeated) {
pm_node_flag_set_repeated_parameter(param);
@@ -14945,6 +14973,10 @@ parse_parameters(
}
}
+ if (!uses_parentheses) {
+ refute_optional_parameter(parser);
+ }
+
if (params->keyword_rest == NULL) {
pm_parameters_node_keyword_rest_set(params, param);
} else {
diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb
index 9a30a57e3b0eca..23732530854e50 100644
--- a/prism/templates/src/diagnostic.c.erb
+++ b/prism/templates/src/diagnostic.c.erb
@@ -144,6 +144,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_CONDITIONAL_WHILE_PREDICATE] = { "expected a predicate expression for the `while` statement", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = { "expected a constant after the `::` operator", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_ENDLESS] = { "could not parse the endless method body", PM_ERROR_LEVEL_SYNTAX },
+ [PM_ERR_DEF_ENDLESS_PARAMETERS] = { "could not parse the endless method parameters", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_ENDLESS_SETTER] = { "invalid method name; a setter method cannot be defined in an endless method definition", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_NAME] = { "unexpected %s; expected a method name", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_DEF_PARAMS_TERM] = { "expected a delimiter to close the parameters", PM_ERROR_LEVEL_SYNTAX },
diff --git a/spec/mspec/lib/mspec/commands/mspec-ci.rb b/spec/mspec/lib/mspec/commands/mspec-ci.rb
index 9959059ca1c7f2..8951572f693685 100644
--- a/spec/mspec/lib/mspec/commands/mspec-ci.rb
+++ b/spec/mspec/lib/mspec/commands/mspec-ci.rb
@@ -18,6 +18,7 @@ def options(argv = ARGV)
options.chdir
options.prefix
options.configure { |f| load f }
+ options.repeat
options.pretend
options.interrupt
options.timeout
diff --git a/spec/mspec/spec/commands/mspec_ci_spec.rb b/spec/mspec/spec/commands/mspec_ci_spec.rb
index bcbc5b422496a9..b8dc9d062f4d61 100644
--- a/spec/mspec/spec/commands/mspec_ci_spec.rb
+++ b/spec/mspec/spec/commands/mspec_ci_spec.rb
@@ -78,6 +78,11 @@
@script.options []
end
+ it "enables the repeat option" do
+ expect(@options).to receive(:repeat)
+ @script.options @argv
+ end
+
it "calls #custom_options" do
expect(@script).to receive(:custom_options).with(@options)
@script.options []
diff --git a/spec/mspec/spec/commands/mspec_run_spec.rb b/spec/mspec/spec/commands/mspec_run_spec.rb
index 62acd49d7fefac..f96be2b43ee66c 100644
--- a/spec/mspec/spec/commands/mspec_run_spec.rb
+++ b/spec/mspec/spec/commands/mspec_run_spec.rb
@@ -105,6 +105,11 @@
@script.options @argv
end
+ it "enables the repeat option" do
+ expect(@options).to receive(:repeat)
+ @script.options @argv
+ end
+
it "exits if there are no files to process and './spec' is not a directory" do
expect(File).to receive(:directory?).with("./spec").and_return(false)
expect(@options).to receive(:parse).and_return([])
diff --git a/spec/mspec/tool/sync/sync-rubyspec.rb b/spec/mspec/tool/sync/sync-rubyspec.rb
index effccc1567c156..617123733e5442 100644
--- a/spec/mspec/tool/sync/sync-rubyspec.rb
+++ b/spec/mspec/tool/sync/sync-rubyspec.rb
@@ -3,7 +3,7 @@
IMPLS = {
truffleruby: {
- git: "https://github.com/oracle/truffleruby.git",
+ git: "https://github.com/truffleruby/truffleruby.git",
from_commit: "f10ab6988d",
},
jruby: {
@@ -34,6 +34,13 @@
SOURCE_REPO = MSPEC ? MSPEC_REPO : RUBYSPEC_REPO
+# LAST_MERGE is a commit of ruby/spec or ruby/mspec
+# which is the spec/mspec commit that was last imported in the Ruby implementation
+# (i.e. the commit in "Update to ruby/spec@commit").
+# It is normally automatically computed, but can be manually set when
+# e.g. the last update of specs wasn't merged in the Ruby implementation.
+LAST_MERGE = ENV["LAST_MERGE"]
+
NOW = Time.now
BRIGHT_RED = "\e[31;1m"
@@ -142,8 +149,8 @@ def rebase_commits(impl)
else
sh "git", "checkout", impl.name
- if ENV["LAST_MERGE"]
- last_merge = `git log -n 1 --format='%H %ct' #{ENV["LAST_MERGE"]}`
+ if LAST_MERGE
+ last_merge = `git log -n 1 --format='%H %ct' #{LAST_MERGE}`
else
last_merge = `git log --grep='^#{impl.last_merge_message}' -n 1 --format='%H %ct'`
end
diff --git a/spec/ruby/.rubocop_todo.yml b/spec/ruby/.rubocop_todo.yml
index 6b43db9b9a4dd7..bd30f3f14af1ba 100644
--- a/spec/ruby/.rubocop_todo.yml
+++ b/spec/ruby/.rubocop_todo.yml
@@ -44,6 +44,7 @@ Lint/FloatOutOfRange:
Lint/ImplicitStringConcatenation:
Exclude:
- 'language/string_spec.rb'
+ - 'core/string/chilled_string_spec.rb'
# Offense count: 4
Lint/IneffectiveAccessModifier:
diff --git a/spec/ruby/README.md b/spec/ruby/README.md
index 56d6c3c5426a7a..674ada4c9e4cc9 100644
--- a/spec/ruby/README.md
+++ b/spec/ruby/README.md
@@ -26,7 +26,7 @@ ruby/spec is known to be tested in these implementations for every commit:
* [MRI](https://rubyci.org/) on 30 platforms and 4 versions
* [JRuby](https://github.com/jruby/jruby/tree/master/spec/ruby) for both 1.7 and 9.x
-* [TruffleRuby](https://github.com/oracle/truffleruby/tree/master/spec/ruby)
+* [TruffleRuby](https://github.com/truffleruby/truffleruby/tree/master/spec/ruby)
* [Opal](https://github.com/opal/opal/tree/master/spec)
* [Artichoke](https://github.com/artichoke/spec/tree/artichoke-vendor)
diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb
index 4941e098f665c1..9e0fe7556a43af 100644
--- a/spec/ruby/core/array/shared/unshift.rb
+++ b/spec/ruby/core/array/shared/unshift.rb
@@ -49,7 +49,7 @@
-> { ArraySpecs.frozen_array.send(@method) }.should raise_error(FrozenError)
end
- # https://github.com/oracle/truffleruby/issues/2772
+ # https://github.com/truffleruby/truffleruby/issues/2772
it "doesn't rely on Array#[]= so it can be overridden" do
subclass = Class.new(Array) do
def []=(*)
diff --git a/spec/ruby/core/io/readpartial_spec.rb b/spec/ruby/core/io/readpartial_spec.rb
index 2fcfaf520370e3..176c33cf9e43cd 100644
--- a/spec/ruby/core/io/readpartial_spec.rb
+++ b/spec/ruby/core/io/readpartial_spec.rb
@@ -93,6 +93,11 @@
@rd.readpartial(0).should == ""
end
+ it "raises IOError if the stream is closed and the length argument is 0" do
+ @rd.close
+ -> { @rd.readpartial(0) }.should raise_error(IOError, "closed stream")
+ end
+
it "clears and returns the given buffer if the length argument is 0" do
buffer = +"existing content"
@rd.readpartial(0, buffer).should == buffer
diff --git a/spec/ruby/core/kernel/Float_spec.rb b/spec/ruby/core/kernel/Float_spec.rb
index 1705205996a9ad..e00fe815726318 100644
--- a/spec/ruby/core/kernel/Float_spec.rb
+++ b/spec/ruby/core/kernel/Float_spec.rb
@@ -163,6 +163,7 @@ def to_f() 1.2 end
-> { @object.send(:Float, "+1.") }.should raise_error(ArgumentError)
-> { @object.send(:Float, "-1.") }.should raise_error(ArgumentError)
-> { @object.send(:Float, "1.e+0") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "1.e-2") }.should raise_error(ArgumentError)
end
end
@@ -172,6 +173,7 @@ def to_f() 1.2 end
@object.send(:Float, "+1.").should == 1.0
@object.send(:Float, "-1.").should == -1.0
@object.send(:Float, "1.e+0").should == 1.0
+ @object.send(:Float, "1.e-2").should be_close(0.01, TOLERANCE)
end
end
diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb
index a68389a7b407a2..2b2c6c9b63ff1e 100644
--- a/spec/ruby/core/kernel/shared/sprintf.rb
+++ b/spec/ruby/core/kernel/shared/sprintf.rb
@@ -449,7 +449,7 @@ def obj.to_str
it "is escaped by %" do
@method.call("%%").should == "%"
- @method.call("%%d", 10).should == "%d"
+ @method.call("%%d").should == "%d"
end
end
end
diff --git a/spec/ruby/core/kernel/singleton_method_spec.rb b/spec/ruby/core/kernel/singleton_method_spec.rb
index 0bdf125ad84898..7d63fa7cc61223 100644
--- a/spec/ruby/core/kernel/singleton_method_spec.rb
+++ b/spec/ruby/core/kernel/singleton_method_spec.rb
@@ -1,7 +1,7 @@
require_relative '../../spec_helper'
describe "Kernel#singleton_method" do
- it "find a method defined on the singleton class" do
+ it "finds a method defined on the singleton class" do
obj = Object.new
def obj.foo; end
obj.singleton_method(:foo).should be_an_instance_of(Method)
@@ -38,4 +38,48 @@ def foo
e.class.should == NameError
}
end
+
+ ruby_bug "#20620", ""..."3.4" do
+ it "finds a method defined in a module included in the singleton class" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.singleton_class.include(m)
+
+ obj.singleton_method(:foo).should be_an_instance_of(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+
+ it "finds a method defined in a module prepended in the singleton class" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.singleton_class.prepend(m)
+
+ obj.singleton_method(:foo).should be_an_instance_of(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+
+ it "finds a method defined in a module that an object is extended with" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.extend(m)
+
+ obj.singleton_method(:foo).should be_an_instance_of(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+ end
end
diff --git a/spec/ruby/core/kernel/sprintf_spec.rb b/spec/ruby/core/kernel/sprintf_spec.rb
index 9ef7f86f16d833..5a4a90ff7a2860 100644
--- a/spec/ruby/core/kernel/sprintf_spec.rb
+++ b/spec/ruby/core/kernel/sprintf_spec.rb
@@ -13,28 +13,52 @@
describe "Kernel#sprintf" do
it_behaves_like :kernel_sprintf, -> format, *args {
- sprintf(format, *args)
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
it_behaves_like :kernel_sprintf_encoding, -> format, *args {
- sprintf(format, *args)
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
it_behaves_like :kernel_sprintf_to_str, -> format, *args {
- sprintf(format, *args)
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
end
describe "Kernel.sprintf" do
it_behaves_like :kernel_sprintf, -> format, *args {
- Kernel.sprintf(format, *args)
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
it_behaves_like :kernel_sprintf_encoding, -> format, *args {
- Kernel.sprintf(format, *args)
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
it_behaves_like :kernel_sprintf_to_str, -> format, *args {
- Kernel.sprintf(format, *args)
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
}
end
diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb
index 00164ad90b2004..e03498c6dc44de 100644
--- a/spec/ruby/core/kernel/warn_spec.rb
+++ b/spec/ruby/core/kernel/warn_spec.rb
@@ -112,6 +112,12 @@
ruby_exe(file, options: "-rrubygems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
end
+ it "doesn't show the caller when the uplevel is `nil`" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4("foo", nil) }.should output(nil, "foo\n")
+ end
+
guard -> { Kernel.instance_method(:tap).source_location } do
it "skips { ModuleSpecs::Autoload::DynClass }.should raise_error(NameError, /private constant/)
+ end
+
# [ruby-core:19127] [ruby-core:29941]
it "does NOT raise a NameError when the autoload file did not define the constant and a module is opened with the same name" do
module ModuleSpecs::Autoload
diff --git a/spec/ruby/core/regexp/compile_spec.rb b/spec/ruby/core/regexp/compile_spec.rb
index c41399cfbb3539..887c8d77dc3c15 100644
--- a/spec/ruby/core/regexp/compile_spec.rb
+++ b/spec/ruby/core/regexp/compile_spec.rb
@@ -14,6 +14,6 @@
it_behaves_like :regexp_new_regexp, :compile
end
-describe "Regexp.new given a non-String/Regexp" do
+describe "Regexp.compile given a non-String/Regexp" do
it_behaves_like :regexp_new_non_string_or_regexp, :compile
end
diff --git a/spec/ruby/core/regexp/new_spec.rb b/spec/ruby/core/regexp/new_spec.rb
index 65f612df55311f..79210e9a236a04 100644
--- a/spec/ruby/core/regexp/new_spec.rb
+++ b/spec/ruby/core/regexp/new_spec.rb
@@ -7,11 +7,11 @@
describe "Regexp.new given a String" do
it_behaves_like :regexp_new_string, :new
+ it_behaves_like :regexp_new_string_binary, :new
end
describe "Regexp.new given a Regexp" do
it_behaves_like :regexp_new_regexp, :new
- it_behaves_like :regexp_new_string_binary, :new
end
describe "Regexp.new given a non-String/Regexp" do
diff --git a/spec/ruby/core/string/to_f_spec.rb b/spec/ruby/core/string/to_f_spec.rb
index a91ccc168ecfee..abfd2517b69327 100644
--- a/spec/ruby/core/string/to_f_spec.rb
+++ b/spec/ruby/core/string/to_f_spec.rb
@@ -127,4 +127,16 @@
}.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
end
end
+
+ it "allows String representation without a fractional part" do
+ "1.".to_f.should == 1.0
+ "+1.".to_f.should == 1.0
+ "-1.".to_f.should == -1.0
+ "1.e+0".to_f.should == 1.0
+ "1.e+0".to_f.should == 1.0
+
+ ruby_bug "#20705", ""..."3.4" do
+ "1.e-2".to_f.should be_close(0.01, TOLERANCE)
+ end
+ end
end
diff --git a/spec/ruby/core/string/unpack1_spec.rb b/spec/ruby/core/string/unpack1_spec.rb
index 3b3b879f75f946..cfb47fe6953365 100644
--- a/spec/ruby/core/string/unpack1_spec.rb
+++ b/spec/ruby/core/string/unpack1_spec.rb
@@ -31,4 +31,17 @@
it "raises an ArgumentError when the offset is larger than the string bytesize" do
-> { "a".unpack1("C", offset: 2) }.should raise_error(ArgumentError, "offset outside of string")
end
+
+ context "with format 'm0'" do
+ # unpack1("m0") takes a special code path that calls Pack.unpackBase46Strict instead of Pack.unpack_m,
+ # which is why we repeat the tests for unpack("m0") here.
+
+ it "decodes base64" do
+ "dGVzdA==".unpack1("m0").should == "test"
+ end
+
+ it "raises an ArgumentError for an invalid base64 character" do
+ -> { "dGV%zdA==".unpack1("m0") }.should raise_error(ArgumentError)
+ end
+ end
end
diff --git a/spec/ruby/core/thread/fixtures/classes.rb b/spec/ruby/core/thread/fixtures/classes.rb
index 54bd85fae30b6e..7c485660a8f522 100644
--- a/spec/ruby/core/thread/fixtures/classes.rb
+++ b/spec/ruby/core/thread/fixtures/classes.rb
@@ -27,6 +27,7 @@ def self.raise(*args, **kwargs, &block)
thread.join
ensure
thread.kill if thread.alive?
+ Thread.pass while thread.alive? # Thread#kill may not terminate a thread immediately so it may be detected as a leaked one
end
end
diff --git a/spec/ruby/core/time/utc_spec.rb b/spec/ruby/core/time/utc_spec.rb
index 3d36e13ccfecdb..ab3c0df657e045 100644
--- a/spec/ruby/core/time/utc_spec.rb
+++ b/spec/ruby/core/time/utc_spec.rb
@@ -43,10 +43,14 @@
it "does not treat time with +00:00 offset as UTC" do
Time.new(2022, 1, 1, 0, 0, 0, "+00:00").utc?.should == false
+ Time.now.localtime("+00:00").utc?.should == false
+ Time.at(Time.now, in: "+00:00").utc?.should == false
end
it "does not treat time with 0 offset as UTC" do
Time.new(2022, 1, 1, 0, 0, 0, 0).utc?.should == false
+ Time.now.localtime(0).utc?.should == false
+ Time.at(Time.now, in: 0).utc?.should == false
end
end
diff --git a/spec/ruby/core/warning/warn_spec.rb b/spec/ruby/core/warning/warn_spec.rb
index 572885c2b4ee6d..12677349874e5b 100644
--- a/spec/ruby/core/warning/warn_spec.rb
+++ b/spec/ruby/core/warning/warn_spec.rb
@@ -97,6 +97,20 @@ def Warning.warn(msg)
end
end
+ ruby_version_is "3.4" do
+ it "warns when category is :strict_unused_block but Warning[:strict_unused_block] is false" do
+ warn_experimental = Warning[:strict_unused_block]
+ Warning[:strict_unused_block] = true
+ begin
+ -> {
+ Warning.warn("foo", category: :strict_unused_block)
+ }.should complain("foo")
+ ensure
+ Warning[:strict_unused_block] = warn_experimental
+ end
+ end
+ end
+
it "doesn't print message when category is :deprecated but Warning[:deprecated] is false" do
warn_deprecated = Warning[:deprecated]
Warning[:deprecated] = false
@@ -121,6 +135,20 @@ def Warning.warn(msg)
end
end
+ ruby_version_is "3.4" do
+ it "doesn't print message when category is :strict_unused_block but Warning[:strict_unused_block] is false" do
+ warn_experimental = Warning[:strict_unused_block]
+ Warning[:strict_unused_block] = false
+ begin
+ -> {
+ Warning.warn("foo", category: :strict_unused_block)
+ }.should_not complain
+ ensure
+ Warning[:strict_unused_block] = warn_experimental
+ end
+ end
+ end
+
ruby_bug '#20573', ''...'3.4' do
it "isn't called by Kernel.warn when category is :deprecated but Warning[:deprecated] is false" do
warn_deprecated = Warning[:deprecated]
diff --git a/spec/ruby/language/assignments_spec.rb b/spec/ruby/language/assignments_spec.rb
index 4ad9f4167bbad0..d469459c43d727 100644
--- a/spec/ruby/language/assignments_spec.rb
+++ b/spec/ruby/language/assignments_spec.rb
@@ -72,6 +72,34 @@ def []=(k, v, &block) @h[k] = v; end
end
end
end
+
+ context "given keyword arguments" do
+ before do
+ @klass = Class.new do
+ attr_reader :x
+
+ def []=(*args, **kw)
+ @x = [args, kw]
+ end
+ end
+ end
+
+ ruby_version_is ""..."3.4" do
+ it "supports keyword arguments in index assignments" do
+ a = @klass.new
+ eval "a[1, 2, 3, b: 4] = 5"
+ a.x.should == [[1, 2, 3, {b: 4}, 5], {}]
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "raies SyntaxError when given keyword arguments in index assignments" do
+ a = @klass.new
+ -> { eval "a[1, 2, 3, b: 4] = 5" }.should raise_error(SyntaxError,
+ /keywords are not allowed in index assignment expressions|keyword arg given in index assignment/) # prism|parse.y
+ end
+ end
+ end
end
describe 'using +=' do
@@ -176,6 +204,46 @@ def []=(k, v, &block) @h[k] = v; end
end
end
+ context "given keyword arguments" do
+ before do
+ @klass = Class.new do
+ attr_reader :x
+
+ def [](*args)
+ 100
+ end
+
+ def []=(*args, **kw)
+ @x = [args, kw]
+ end
+ end
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "supports keyword arguments in index assignments" do
+ a = @klass.new
+ eval "a[1, 2, 3, b: 4] += 5"
+ a.x.should == [[1, 2, 3, {b: 4}, 105], {}]
+ end
+ end
+
+ ruby_version_is "3.3"..."3.4" do
+ it "supports keyword arguments in index assignments" do
+ a = @klass.new
+ eval "a[1, 2, 3, b: 4] += 5"
+ a.x.should == [[1, 2, 3, 105], {b: 4}]
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "raies SyntaxError when given keyword arguments in index assignments" do
+ a = @klass.new
+ -> { eval "a[1, 2, 3, b: 4] += 5" }.should raise_error(SyntaxError,
+ /keywords are not allowed in index assignment expressions|keyword arg given in index assignment/) # prism|parse.y
+ end
+ end
+ end
+
context 'splatted argument' do
it 'correctly handles it' do
@b[:m] = 10
diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb
index 1d412a133edc30..39b245fe2a9de2 100644
--- a/spec/ruby/language/method_spec.rb
+++ b/spec/ruby/language/method_spec.rb
@@ -1487,3 +1487,163 @@ def greet(person) = "Hi, ".dup.concat person
greet("Homer").should == "Hi, Homer"
end
end
+
+describe "warning about not used block argument" do
+ ruby_version_is "3.4" do
+ it "warns when passing a block argument to a method that never uses it" do
+ def m_that_does_not_use_block
+ 42
+ end
+
+ -> {
+ m_that_does_not_use_block { }
+ }.should complain(
+ /#{__FILE__}:#{__LINE__ - 2}: warning: the block passed to 'm_that_does_not_use_block' defined at #{__FILE__}:#{__LINE__ - 7} may be ignored/,
+ verbose: true)
+ end
+
+ it "does not warn when passing a block argument to a method that declares a block parameter" do
+ def m_with_block_parameter(&block)
+ 42
+ end
+
+ -> { m_with_block_parameter { } }.should_not complain(verbose: true)
+ end
+
+ it "does not warn when passing a block argument to a method that declares an anonymous block parameter" do
+ def m_with_anonymous_block_parameter(&)
+ 42
+ end
+
+ -> { m_with_anonymous_block_parameter { } }.should_not complain(verbose: true)
+ end
+
+ it "does not warn when passing a block argument to a method that yields an implicit block parameter" do
+ def m_with_yield
+ yield 42
+ end
+
+ -> { m_with_yield { } }.should_not complain(verbose: true)
+ end
+
+ it "warns when passing a block argument to a method that calls #block_given?" do
+ def m_with_block_given
+ block_given?
+ end
+
+ -> {
+ m_with_block_given { }
+ }.should complain(
+ /#{__FILE__}:#{__LINE__ - 2}: warning: the block passed to 'm_with_block_given' defined at #{__FILE__}:#{__LINE__ - 7} may be ignored/,
+ verbose: true)
+ end
+
+ it "does not warn when passing a block argument to a method that calls super" do
+ parent = Class.new do
+ def m
+ end
+ end
+
+ child = Class.new(parent) do
+ def m
+ super
+ end
+ end
+
+ obj = child.new
+ -> { obj.m { } }.should_not complain(verbose: true)
+ end
+
+ it "does not warn when passing a block argument to a method that calls super(...)" do
+ parent = Class.new do
+ def m(a)
+ end
+ end
+
+ child = Class.new(parent) do
+ def m(...)
+ super(...)
+ end
+ end
+
+ obj = child.new
+ -> { obj.m(42) { } }.should_not complain(verbose: true)
+ end
+
+ it "does not warn when called #initialize()" do
+ klass = Class.new do
+ def initialize
+ end
+ end
+
+ -> { klass.new {} }.should_not complain(verbose: true)
+ end
+
+ it "does not warn when passing a block argument to a method that calls super()" do
+ parent = Class.new do
+ def m
+ end
+ end
+
+ child = Class.new(parent) do
+ def m
+ super()
+ end
+ end
+
+ obj = child.new
+ -> { obj.m { } }.should_not complain(verbose: true)
+ end
+
+ it "warns only once per call site" do
+ def m_that_does_not_use_block
+ 42
+ end
+
+ def call_m_that_does_not_use_block
+ m_that_does_not_use_block {}
+ end
+
+ -> {
+ m_that_does_not_use_block { }
+ }.should complain(/the block passed to 'm_that_does_not_use_block' defined at .+ may be ignored/, verbose: true)
+
+ -> {
+ m_that_does_not_use_block { }
+ }.should_not complain(verbose: true)
+ end
+
+ it "can be disabled with :strict_unused_block warning category" do
+ def m_that_does_not_use_block
+ 42
+ end
+
+ # ensure that warning is emitted
+ -> { m_that_does_not_use_block { } }.should complain(verbose: true)
+
+ warn_experimental = Warning[:strict_unused_block]
+ Warning[:strict_unused_block] = false
+ begin
+ -> { m_that_does_not_use_block { } }.should_not complain(verbose: true)
+ ensure
+ Warning[:strict_unused_block] = warn_experimental
+ end
+ end
+
+ it "can be enabled with :strict_unused_block = true warning category in not verbose mode" do
+ def m_that_does_not_use_block
+ 42
+ end
+
+ warn_experimental = Warning[:strict_unused_block]
+ Warning[:strict_unused_block] = true
+ begin
+ -> {
+ m_that_does_not_use_block { }
+ }.should complain(/the block passed to 'm_that_does_not_use_block' defined at .+ may be ignored/)
+ ensure
+ Warning[:strict_unused_block] = warn_experimental
+ end
+ end
+ end
+end
diff --git a/spec/ruby/library/socket/socket/getaddrinfo_spec.rb b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
index 9f049597d0d2b4..a2330715ad9713 100644
--- a/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
+++ b/spec/ruby/library/socket/socket/getaddrinfo_spec.rb
@@ -106,6 +106,24 @@
]
res.each { |a| expected.should include(a) }
end
+
+ ruby_version_is ""..."3.3" do
+ it "raises SocketError when fails to resolve address" do
+ -> {
+ Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX")
+ }.should raise_error(SocketError)
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raises ResolutionError when fails to resolve address" do
+ -> {
+ Socket.getaddrinfo("www.kame.net", 80, "AF_UNIX")
+ }.should raise_error(Socket::ResolutionError) { |e|
+ e.error_code.should == Socket::EAI_FAMILY
+ }
+ end
+ end
end
end
diff --git a/spec/ruby/library/socket/socket/getnameinfo_spec.rb b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
index 4f13bf484df93e..b3105244a28f70 100644
--- a/spec/ruby/library/socket/socket/getnameinfo_spec.rb
+++ b/spec/ruby/library/socket/socket/getnameinfo_spec.rb
@@ -60,6 +60,24 @@ def should_be_valid_dns_name(name)
name_info = Socket.getnameinfo ["AF_INET", 9, 'foo', '127.0.0.1']
name_info[1].should == 'discard'
end
+
+ ruby_version_is ""..."3.3" do
+ it "raises SocketError when fails to resolve address" do
+ -> {
+ Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"])
+ }.should raise_error(SocketError)
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raises ResolutionError when fails to resolve address" do
+ -> {
+ Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"])
+ }.should raise_error(Socket::ResolutionError) { |e|
+ e.error_code.should == Socket::EAI_FAMILY
+ }
+ end
+ end
end
describe 'Socket.getnameinfo' do
diff --git a/spec/ruby/library/stringio/readpartial_spec.rb b/spec/ruby/library/stringio/readpartial_spec.rb
index f25cef40148137..988c552235d5e6 100644
--- a/spec/ruby/library/stringio/readpartial_spec.rb
+++ b/spec/ruby/library/stringio/readpartial_spec.rb
@@ -10,13 +10,7 @@
@string.close unless @string.closed?
end
- it "raises IOError on closed stream" do
- @string.close
- -> { @string.readpartial(10) }.should raise_error(IOError)
- end
-
it "reads at most the specified number of bytes" do
-
# buffered read
@string.read(1).should == 'S'
# return only specified number, not the whole buffer
@@ -67,14 +61,34 @@
it "raises IOError if the stream is closed" do
@string.close
- -> { @string.readpartial(1) }.should raise_error(IOError)
+ -> { @string.readpartial(1) }.should raise_error(IOError, "not opened for reading")
end
it "raises ArgumentError if the negative argument is provided" do
- -> { @string.readpartial(-1) }.should raise_error(ArgumentError)
+ -> { @string.readpartial(-1) }.should raise_error(ArgumentError, "negative length -1 given")
end
it "immediately returns an empty string if the length argument is 0" do
@string.readpartial(0).should == ""
end
+
+ it "raises IOError if the stream is closed and the length argument is 0" do
+ @string.close
+ -> { @string.readpartial(0) }.should raise_error(IOError, "not opened for reading")
+ end
+
+ it "clears and returns the given buffer if the length argument is 0" do
+ buffer = +"existing content"
+ @string.readpartial(0, buffer).should == buffer
+ buffer.should == ""
+ end
+
+ version_is StringIO::VERSION, "3.1.2" do # ruby_version_is "3.4"
+ it "preserves the encoding of the given buffer" do
+ buffer = ''.encode(Encoding::ISO_8859_1)
+ @string.readpartial(10, buffer)
+
+ buffer.encoding.should == Encoding::ISO_8859_1
+ end
+ end
end
diff --git a/spec/ruby/optional/capi/ext/set_spec.c b/spec/ruby/optional/capi/ext/set_spec.c
index 72938be1d887f0..7af922fd49ea96 100644
--- a/spec/ruby/optional/capi/ext/set_spec.c
+++ b/spec/ruby/optional/capi/ext/set_spec.c
@@ -47,6 +47,7 @@ VALUE set_spec_rb_set_size(VALUE self, VALUE set) {
void Init_set_spec(void) {
VALUE cls = rb_define_class("CApiSetSpecs", rb_cObject);
+
rb_define_method(cls, "rb_set_foreach", set_spec_rb_set_foreach, 2);
rb_define_method(cls, "rb_set_new", set_spec_rb_set_new, 0);
rb_define_method(cls, "rb_set_new_capa", set_spec_rb_set_new_capa, 1);
diff --git a/spec/ruby/shared/kernel/raise.rb b/spec/ruby/shared/kernel/raise.rb
index d46c4b7b15f07e..8432c835946d6e 100644
--- a/spec/ruby/shared/kernel/raise.rb
+++ b/spec/ruby/shared/kernel/raise.rb
@@ -191,8 +191,8 @@ def e.exception
-> do
@object.raise(error, cause: error)
- end.should raise_error(RuntimeError, "message") do |error|
- error.cause.should == nil
+ end.should raise_error(RuntimeError, "message") do |e|
+ e.cause.should == nil
end
end
diff --git a/test/openssl/test_provider.rb b/test/openssl/test_provider.rb
index 6f85c00c988204..10081e208ced1b 100644
--- a/test/openssl/test_provider.rb
+++ b/test/openssl/test_provider.rb
@@ -46,6 +46,7 @@ def test_openssl_legacy_provider
with_openssl(<<-'end;')
begin
+ OpenSSL::Provider.load("default")
OpenSSL::Provider.load("legacy")
rescue OpenSSL::Provider::ProviderError
omit "Only for OpenSSL with legacy provider"
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 7abe2c6df5d330..3ec1a710436f38 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -2129,11 +2129,7 @@ def test_connect_works_when_setting_dh_callback_to_nil
ctx.tmp_dh_callback = nil
}
start_server(ctx_proc: ctx_proc) do |port|
- EnvUtil.suppress_warning { # uses default callback
- assert_nothing_raised {
- server_connect(port) { }
- }
- }
+ assert_nothing_raised { server_connect(port) { } }
end
end
@@ -2321,6 +2317,50 @@ def test_export_keying_material
end
end
+ # OpenSSL::Buffering requires $/ accessible from non-main Ractors (Ruby 3.5)
+ # https://bugs.ruby-lang.org/issues/21109
+ #
+ # Hangs on Windows
+ # https://bugs.ruby-lang.org/issues/21537
+ if respond_to?(:ractor) && RUBY_VERSION >= "3.5" && RUBY_PLATFORM !~ /mswin|mingw/
+ ractor
+ def test_ractor_client
+ start_server { |port|
+ s = Ractor.new(port, @ca_cert) { |port, ca_cert|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.cert_store = OpenSSL::X509::Store.new.tap { |store|
+ store.add_cert(ca_cert)
+ }
+ begin
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.connect
+ ssl.puts("abc")
+ ssl.gets
+ ensure
+ ssl.close
+ sock.close
+ end
+ }.value
+ assert_equal("abc\n", s)
+ }
+ end
+
+ ractor
+ def test_ractor_set_params
+ # We cannot actually test default stores in the test suite as it depends
+ # on the environment, but at least check that it does not raise an
+ # exception
+ ok = Ractor.new {
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params
+ ctx.cert_store.kind_of?(OpenSSL::X509::Store)
+ }.value
+ assert(ok, "ctx.cert_store is an instance of OpenSSL::X509::Store")
+ end
+ end
+
private
def server_connect(port, ctx = nil)
diff --git a/test/prism/errors/def_with_optional_splat.txt b/test/prism/errors/def_with_optional_splat.txt
new file mode 100644
index 00000000000000..74a833ceec0dac
--- /dev/null
+++ b/test/prism/errors/def_with_optional_splat.txt
@@ -0,0 +1,6 @@
+def foo(*bar = nil); end
+ ^ unexpected '='; expected a `)` to close the parameters
+ ^ unexpected ')', expecting end-of-input
+ ^ unexpected ')', ignoring it
+ ^~~ unexpected 'end', ignoring it
+
diff --git a/test/prism/errors/endless_method_command_call_parameters.txt b/test/prism/errors/endless_method_command_call_parameters.txt
new file mode 100644
index 00000000000000..94c4f88fc835df
--- /dev/null
+++ b/test/prism/errors/endless_method_command_call_parameters.txt
@@ -0,0 +1,24 @@
+def f x: = 1
+ ^~ could not parse the endless method parameters
+
+def f ... = 1
+ ^~~ could not parse the endless method parameters
+
+def f * = 1
+ ^ could not parse the endless method parameters
+
+def f ** = 1
+ ^~ could not parse the endless method parameters
+
+def f & = 1
+ ^ could not parse the endless method parameters
+
+def f *a = 1
+ ^ could not parse the endless method parameters
+
+def f **a = 1
+ ^ could not parse the endless method parameters
+
+def f &a = 1
+ ^ could not parse the endless method parameters
+
diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb
index 5a8463ad8e719e..bedf243b3e5919 100644
--- a/test/zlib/test_zlib.rb
+++ b/test/zlib/test_zlib.rb
@@ -9,6 +9,9 @@
begin
require 'zlib'
rescue LoadError
+else
+ z = "/zlib.#{RbConfig::CONFIG["DLEXT"]}"
+ LOADED_ZLIB, = $".select {|f| f.end_with?(z)}
end
if defined? Zlib
@@ -1525,7 +1528,7 @@ def test_gunzip_encoding
end
def test_gunzip_no_memory_leak
- assert_no_memory_leak(%[-rzlib], "#{<<~"{#"}", "#{<<~'};'}")
+ assert_no_memory_leak(%W[-r#{LOADED_ZLIB}], "#{<<~"{#"}", "#{<<~'};'}")
d = Zlib.gzip("data")
{#
10_000.times {Zlib.gunzip(d)}
diff --git a/zjit/src/options.rs b/zjit/src/options.rs
index 44209b963f3bf9..4ef998acede36c 100644
--- a/zjit/src/options.rs
+++ b/zjit/src/options.rs
@@ -6,24 +6,26 @@ use crate::cruby::*;
use std::collections::HashSet;
/// Default --zjit-num-profiles
-const DEFAULT_NUM_PROFILES: u32 = 5;
+const DEFAULT_NUM_PROFILES: NumProfiles = 5;
+pub type NumProfiles = u32;
/// Default --zjit-call-threshold. This should be large enough to avoid compiling
/// warmup code, but small enough to perform well on micro-benchmarks.
-pub const DEFAULT_CALL_THRESHOLD: u64 = 30;
+pub const DEFAULT_CALL_THRESHOLD: CallThreshold = 30;
+pub type CallThreshold = u64;
/// Number of calls to start profiling YARV instructions.
/// They are profiled `rb_zjit_call_threshold - rb_zjit_profile_threshold` times,
/// which is equal to --zjit-num-profiles.
#[unsafe(no_mangle)]
#[allow(non_upper_case_globals)]
-pub static mut rb_zjit_profile_threshold: u64 = DEFAULT_CALL_THRESHOLD - DEFAULT_NUM_PROFILES as u64;
+pub static mut rb_zjit_profile_threshold: CallThreshold = DEFAULT_CALL_THRESHOLD - DEFAULT_NUM_PROFILES as CallThreshold;
/// Number of calls to compile ISEQ with ZJIT at jit_compile() in vm.c.
/// --zjit-call-threshold=1 compiles on first execution without profiling information.
#[unsafe(no_mangle)]
#[allow(non_upper_case_globals)]
-pub static mut rb_zjit_call_threshold: u64 = DEFAULT_CALL_THRESHOLD;
+pub static mut rb_zjit_call_threshold: CallThreshold = DEFAULT_CALL_THRESHOLD;
/// ZJIT command-line options. This is set before rb_zjit_init() sets
/// ZJITState so that we can query some options while loading builtins.
@@ -40,7 +42,7 @@ pub struct Options {
pub mem_bytes: usize,
/// Number of times YARV instructions should be profiled.
- pub num_profiles: u32,
+ pub num_profiles: NumProfiles,
/// Enable YJIT statsitics
pub stats: bool,
@@ -319,14 +321,14 @@ fn update_profile_threshold() {
unsafe { rb_zjit_profile_threshold = 0; }
} else {
// Otherwise, profile instructions at least once.
- let num_profiles = get_option!(num_profiles) as u64;
- unsafe { rb_zjit_profile_threshold = rb_zjit_call_threshold.saturating_sub(num_profiles).max(1) };
+ let num_profiles = get_option!(num_profiles);
+ unsafe { rb_zjit_profile_threshold = rb_zjit_call_threshold.saturating_sub(num_profiles.into()).max(1) };
}
}
/// Update --zjit-call-threshold for testing
#[cfg(test)]
-pub fn set_call_threshold(call_threshold: u64) {
+pub fn set_call_threshold(call_threshold: CallThreshold) {
unsafe { rb_zjit_call_threshold = call_threshold; }
rb_zjit_prepare_options();
update_profile_threshold();
diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs
index 471ff89e763148..9588a541827f35 100644
--- a/zjit/src/profile.rs
+++ b/zjit/src/profile.rs
@@ -3,7 +3,7 @@
// We use the YARV bytecode constants which have a CRuby-style name
#![allow(non_upper_case_globals)]
-use crate::{cruby::*, gc::get_or_create_iseq_payload, options::get_option};
+use crate::{cruby::*, gc::get_or_create_iseq_payload, options::{get_option, NumProfiles}};
use crate::distribution::{Distribution, DistributionSummary};
use crate::stats::Counter::profile_time_ns;
use crate::stats::with_time_stat;
@@ -278,7 +278,7 @@ pub struct IseqProfile {
opnd_types: Vec>,
/// Number of profiled executions for each YARV instruction, indexed by the instruction index
- num_profiles: Vec,
+ num_profiles: Vec,
}
impl IseqProfile {