summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast.c3
-rw-r--r--bignum.c10
-rw-r--r--class.c31
-rw-r--r--common.mk2
-rw-r--r--compile.c243
-rw-r--r--configure.ac31
-rw-r--r--doc/syntax/methods.rdoc15
-rw-r--r--enumerator.c98
-rw-r--r--eval.c66
-rw-r--r--eval_error.c6
-rw-r--r--ext/date/date.gemspec7
-rw-r--r--ext/date/date_core.c400
-rw-r--r--ext/date/date_parse.c39
-rw-r--r--ext/date/date_strptime.c6
-rw-r--r--ext/date/lib/date.rb1
-rw-r--r--ext/fiddle/closure.c5
-rw-r--r--ext/fiddle/extconf.rb15
-rw-r--r--ext/objspace/objspace.c5
-rw-r--r--ext/win32ole/win32ole.c3
-rw-r--r--ext/win32ole/win32ole.h4
-rw-r--r--ext/win32ole/win32ole_error.c3
-rw-r--r--ext/win32ole/win32ole_error.h4
-rw-r--r--ext/win32ole/win32ole_method.c2
-rw-r--r--ext/win32ole/win32ole_method.h2
-rw-r--r--ext/win32ole/win32ole_record.c2
-rw-r--r--ext/win32ole/win32ole_record.h2
-rw-r--r--ext/win32ole/win32ole_type.c2
-rw-r--r--ext/win32ole/win32ole_type.h2
-rw-r--r--ext/win32ole/win32ole_typelib.c2
-rw-r--r--ext/win32ole/win32ole_typelib.h2
-rw-r--r--ext/win32ole/win32ole_variable.c2
-rw-r--r--ext/win32ole/win32ole_variable.h2
-rw-r--r--ext/win32ole/win32ole_variant.c2
-rw-r--r--ext/win32ole/win32ole_variant.h2
-rw-r--r--ext/win32ole/win32ole_variant_m.c2
-rw-r--r--ext/win32ole/win32ole_variant_m.h2
-rw-r--r--file.c51
-rw-r--r--hash.c22
-rw-r--r--internal.h13
-rw-r--r--lib/cgi/cookie.rb1
-rw-r--r--lib/fileutils.rb3
-rw-r--r--lib/net/ftp.rb15
-rw-r--r--lib/net/http/response.rb5
-rw-r--r--lib/net/imap.rb8
-rw-r--r--lib/rdoc/rdoc.rb2
-rw-r--r--lib/rdoc/version.rb2
-rw-r--r--lib/resolv.rb4
-rw-r--r--lib/rexml/doctype.rb71
-rw-r--r--lib/rexml/parsers/baseparser.rb178
-rw-r--r--lib/rexml/rexml.rb2
-rw-r--r--lib/rubygems.rb2
-rw-r--r--lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem23
-rw-r--r--lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem25
-rw-r--r--lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem (renamed from lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem)0
-rw-r--r--lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem21
-rw-r--r--lib/tmpdir.rb2
-rw-r--r--lib/uri/ldap.rb1
-rw-r--r--lib/webrick/httprequest.rb6
-rw-r--r--lib/webrick/version.rb2
-rw-r--r--numeric.c59
-rw-r--r--object.c31
-rw-r--r--parse.y52
-rw-r--r--proc.c40
-rw-r--r--process.c265
-rw-r--r--rational.c36
-rw-r--r--spec/ruby/core/method/shared/to_s.rb18
-rw-r--r--spec/ruby/library/net/http/http/get_spec.rb69
-rw-r--r--strftime.c2
-rw-r--r--test/-ext-/debug/test_profile_frames.rb40
-rw-r--r--test/cgi/test_cgi_cookie.rb5
-rw-r--r--test/coverage/test_coverage.rb12
-rw-r--r--test/date/test_date_parse.rb92
-rw-r--r--test/net/ftp/test_ftp.rb159
-rw-r--r--test/net/http/test_https.rb6
-rw-r--r--test/net/imap/test_imap.rb39
-rw-r--r--test/objspace/test_objspace.rb2
-rw-r--r--test/pathname/test_pathname.rb2
-rw-r--r--test/rdoc/test_rdoc_rdoc.rb12
-rw-r--r--test/resolv/test_dns.rb113
-rw-r--r--test/rexml/parse/test_document_type_declaration.rb193
-rw-r--r--test/rexml/parse/test_element.rb26
-rw-r--r--test/rexml/parse/test_notation_declaration.rb181
-rw-r--r--test/rexml/parse/test_processing_instruction.rb19
-rw-r--r--test/rexml/parser/test_ultra_light.rb1
-rw-r--r--test/rexml/test_core.rb4
-rw-r--r--test/rexml/test_doctype.rb10
-rw-r--r--test/ripper/test_parser_events.rb6
-rw-r--r--test/ruby/test_arithmetic_sequence.rb5
-rw-r--r--test/ruby/test_ast.rb11
-rw-r--r--test/ruby/test_class.rb52
-rw-r--r--test/ruby/test_defined.rb46
-rw-r--r--test/ruby/test_env.rb2
-rw-r--r--test/ruby/test_exception.rb20
-rw-r--r--test/ruby/test_float.rb18
-rw-r--r--test/ruby/test_integer.rb3
-rw-r--r--test/ruby/test_keyword.rb6
-rw-r--r--test/ruby/test_method.rb101
-rw-r--r--test/ruby/test_notimp.rb19
-rw-r--r--test/ruby/test_rational.rb7
-rw-r--r--test/ruby/test_regexp.rb11
-rw-r--r--test/ruby/test_syntax.rb15
-rw-r--r--test/ruby/test_time.rb15
-rw-r--r--test/ruby/test_time_tz.rb12
-rw-r--r--test/ruby/test_transcode.rb8
-rw-r--r--test/rubygems/test_bundled_ca.rb11
-rw-r--r--test/test_tmpdir.rb38
-rw-r--r--test/uri/test_ldap.rb4
-rw-r--r--test/webrick/test_httpproxy.rb2
-rw-r--r--test/webrick/test_httpserver.rb2
-rw-r--r--test/webrick/test_server.rb2
-rw-r--r--time.c14
-rw-r--r--tool/m4/ruby_universal_arch.m43
-rwxr-xr-xtool/make-snapshot13
-rw-r--r--tool/pure_parser.rb20
-rw-r--r--transcode.c8
-rw-r--r--util.c3
-rw-r--r--variable.c17
-rw-r--r--version.h12
-rw-r--r--vm.c6
-rw-r--r--vm_args.c11
-rw-r--r--vm_backtrace.c2
-rw-r--r--vm_insnhelper.c8
122 files changed, 2876 insertions, 629 deletions
diff --git a/ast.c b/ast.c
index f0e8dd2eaf..883a838b78 100644
--- a/ast.c
+++ b/ast.c
@@ -443,9 +443,10 @@ node_children(rb_ast_t *ast, NODE *node)
NEW_CHILD(ast, node->nd_args->nd_head),
NEW_CHILD(ast, node->nd_args->nd_body));
case NODE_OP_ASGN2:
- return rb_ary_new_from_args(4, NEW_CHILD(ast, node->nd_recv),
+ return rb_ary_new_from_args(5, NEW_CHILD(ast, node->nd_recv),
node->nd_next->nd_aid ? Qtrue : Qfalse,
ID2SYM(node->nd_next->nd_vid),
+ ID2SYM(node->nd_next->nd_mid),
NEW_CHILD(ast, node->nd_value));
case NODE_OP_ASGN_AND:
return rb_ary_new_from_args(3, NEW_CHILD(ast, node->nd_head), ID2SYM(idANDOP),
diff --git a/bignum.c b/bignum.c
index ee3b49fd04..302774b6e2 100644
--- a/bignum.c
+++ b/bignum.c
@@ -6875,7 +6875,15 @@ estimate_initial_sqrt(VALUE *xp, const size_t xn, const BDIGIT *nds, size_t len)
rshift /= 2;
rshift += (2-(len&1))*BITSPERDIG/2;
if (rshift >= 0) {
- d <<= rshift;
+ if (nlz((BDIGIT)d) + rshift >= BITSPERDIG) {
+ /* (d << rshift) does cause overflow.
+ * example: Integer.sqrt(0xffff_ffff_ffff_ffff ** 2)
+ */
+ d = ~(BDIGIT_DBL)0;
+ }
+ else {
+ d <<= rshift;
+ }
}
BDIGITS_ZERO(xds, xn-2);
bdigitdbl2bary(&xds[xn-2], 2, d);
diff --git a/class.c b/class.c
index 9244f020f3..3936c6a540 100644
--- a/class.c
+++ b/class.c
@@ -32,6 +32,9 @@
#define id_attached id__attached__
+#define METACLASS_OF(k) RBASIC(k)->klass
+#define SET_METACLASS_OF(k, cls) RBASIC_SET_CLASS(k, cls)
+
void
rb_class_subclass_add(VALUE super, VALUE klass)
{
@@ -367,22 +370,35 @@ rb_singleton_class_clone(VALUE obj)
return rb_singleton_class_clone_and_attach(obj, Qundef);
}
+// Clone and return the singleton class of `obj` if it has been created and is attached to `obj`.
VALUE
rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
{
const VALUE klass = RBASIC(obj)->klass;
- if (!FL_TEST(klass, FL_SINGLETON))
- return klass;
+ // Note that `rb_singleton_class()` can create situations where `klass` is
+ // attached to an object other than `obj`. In which case `obj` does not have
+ // a material singleton class attached yet and there is no singleton class
+ // to clone.
+ if (!(FL_TEST(klass, FL_SINGLETON) && rb_attr_get(klass, id_attached) == obj)) {
+ // nothing to clone
+ return klass;
+ }
else {
/* copy singleton(unnamed) class */
+ bool klass_of_clone_is_new;
VALUE clone = class_alloc(RBASIC(klass)->flags, 0);
if (BUILTIN_TYPE(obj) == T_CLASS) {
+ klass_of_clone_is_new = true;
RBASIC_SET_CLASS(clone, clone);
}
else {
- RBASIC_SET_CLASS(clone, rb_singleton_class_clone(klass));
+ VALUE klass_metaclass_clone = rb_singleton_class_clone(klass);
+ // When `METACLASS_OF(klass) == klass_metaclass_clone`, it means the
+ // recursive call did not clone `METACLASS_OF(klass)`.
+ klass_of_clone_is_new = (METACLASS_OF(klass) != klass_metaclass_clone);
+ RBASIC_SET_CLASS(clone, klass_metaclass_clone);
}
RCLASS_SET_SUPER(clone, RCLASS_SUPER(klass));
@@ -406,7 +422,9 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach)
arg.new_klass = clone;
rb_id_table_foreach(RCLASS_M_TBL(klass), clone_method_i, &arg);
}
- rb_singleton_class_attached(RBASIC(clone)->klass, clone);
+ if (klass_of_clone_is_new) {
+ rb_singleton_class_attached(RBASIC(clone)->klass, clone);
+ }
FL_SET(clone, FL_SINGLETON);
return clone;
@@ -428,11 +446,6 @@ rb_singleton_class_attached(VALUE klass, VALUE obj)
}
}
-
-
-#define METACLASS_OF(k) RBASIC(k)->klass
-#define SET_METACLASS_OF(k, cls) RBASIC_SET_CLASS(k, cls)
-
/*!
* whether k is a meta^(n)-class of Class class
* @retval 1 if \a k is a meta^(n)-class of Class class (n >= 0)
diff --git a/common.mk b/common.mk
index c5947ff766..705a1020bf 100644
--- a/common.mk
+++ b/common.mk
@@ -845,6 +845,7 @@ PHONY:
{$(srcdir)}.y.c:
$(ECHO) generating $@
$(Q)$(BASERUBY) $(srcdir)/tool/id2token.rb --path-separator=.$(PATH_SEPARATOR)./ --vpath=$(VPATH) id.h $(SRC_FILE) > parse.tmp.y
+ $(Q)$(BASERUBY) $(srcdir)/tool/pure_parser.rb parse.tmp.y $(YACC)
$(Q)$(YACC) -d $(YFLAGS) -o y.tab.c parse.tmp.y
$(Q)$(RM) parse.tmp.y
$(Q)sed -f $(srcdir)/tool/ytab.sed -e "/^#/s|parse\.tmp\.[iy]|$(SRC_FILE)|" -e "/^#/s!y\.tab\.c!$@!" y.tab.c > $@.new
@@ -2985,6 +2986,7 @@ transcode.$(OBJEXT): $(top_srcdir)/include/ruby.h
transcode.$(OBJEXT): {$(VPATH)}config.h
transcode.$(OBJEXT): {$(VPATH)}defines.h
transcode.$(OBJEXT): {$(VPATH)}encoding.h
+transcode.$(OBJEXT): {$(VPATH)}id.h
transcode.$(OBJEXT): {$(VPATH)}intern.h
transcode.$(OBJEXT): {$(VPATH)}internal.h
transcode.$(OBJEXT): {$(VPATH)}io.h
diff --git a/compile.c b/compile.c
index 4ceb8951b7..f5939efaeb 100644
--- a/compile.c
+++ b/compile.c
@@ -2724,7 +2724,8 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
ELEM_REMOVE(&iobj->link);
return COMPILE_OK;
}
- else if (iobj != diobj && IS_INSN_ID(diobj, jump) &&
+ else if (iobj != diobj && IS_INSN(&diobj->link) &&
+ IS_INSN_ID(diobj, jump) &&
OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0)) {
/*
* useless jump elimination:
@@ -2741,7 +2742,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
goto again;
}
else if (IS_INSN_ID(diobj, leave)) {
- INSN *pop;
/*
* jump LABEL
* ...
@@ -2749,7 +2749,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* leave
* =>
* leave
- * pop
* ...
* LABEL:
* leave
@@ -2759,9 +2758,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
iobj->insn_id = BIN(leave);
iobj->operand_size = 0;
iobj->insn_info = diobj->insn_info;
- /* adjust stack depth */
- pop = new_insn_body(iseq, diobj->insn_info.line_no, BIN(pop), 0);
- ELEM_INSERT_NEXT(&iobj->link, &pop->link);
goto again;
}
else if (IS_INSN(iobj->link.prev) &&
@@ -2867,109 +2863,135 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
* if L2
*/
INSN *nobj = (INSN *)get_destination_insn(iobj);
- INSN *pobj = (INSN *)iobj->link.prev;
- int prev_dup = 0;
- if (pobj) {
- if (!IS_INSN(&pobj->link))
- pobj = 0;
- else if (IS_INSN_ID(pobj, dup))
- prev_dup = 1;
- }
-
- for (;;) {
- if (IS_INSN_ID(nobj, jump)) {
- replace_destination(iobj, nobj);
- }
- else if (prev_dup && IS_INSN_ID(nobj, dup) &&
- !!(nobj = (INSN *)nobj->link.next) &&
- /* basic blocks, with no labels in the middle */
- nobj->insn_id == iobj->insn_id) {
- /*
- * dup
- * if L1
- * ...
- * L1:
- * dup
- * if L2
- * =>
- * dup
- * if L2
- * ...
- * L1:
- * dup
- * if L2
- */
- replace_destination(iobj, nobj);
- }
- else if (pobj) {
- /*
- * putnil
- * if L1
- * =>
- * # nothing
- *
- * putobject true
- * if L1
- * =>
- * jump L1
- *
- * putstring ".."
- * if L1
- * =>
- * jump L1
- *
- * putstring ".."
- * dup
- * if L1
- * =>
- * putstring ".."
- * jump L1
- *
- */
- int cond;
- if (prev_dup && IS_INSN(pobj->link.prev)) {
- pobj = (INSN *)pobj->link.prev;
- }
- if (IS_INSN_ID(pobj, putobject)) {
- cond = (IS_INSN_ID(iobj, branchif) ?
- OPERAND_AT(pobj, 0) != Qfalse :
- IS_INSN_ID(iobj, branchunless) ?
- OPERAND_AT(pobj, 0) == Qfalse :
- FALSE);
- }
- else if (IS_INSN_ID(pobj, putstring) ||
- IS_INSN_ID(pobj, duparray) ||
- IS_INSN_ID(pobj, newarray)) {
- cond = IS_INSN_ID(iobj, branchif);
- }
- else if (IS_INSN_ID(pobj, putnil)) {
- cond = !IS_INSN_ID(iobj, branchif);
- }
- else break;
- if (prev_dup || !IS_INSN_ID(pobj, newarray)) {
- ELEM_REMOVE(iobj->link.prev);
- }
- else if (!iseq_pop_newarray(iseq, pobj)) {
- pobj = new_insn_core(iseq, pobj->insn_info.line_no, BIN(pop), 0, NULL);
- ELEM_INSERT_PREV(&iobj->link, &pobj->link);
- }
- if (cond) {
- if (prev_dup) {
- pobj = new_insn_core(iseq, pobj->insn_info.line_no, BIN(putnil), 0, NULL);
- ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
- }
- iobj->insn_id = BIN(jump);
- goto again;
- }
- else {
- unref_destination(iobj, 0);
- ELEM_REMOVE(&iobj->link);
- }
- break;
- }
- else break;
- nobj = (INSN *)get_destination_insn(nobj);
- }
+
+ /* This is super nasty hack!!!
+ *
+ * This jump-jump optimization may ignore event flags of the jump
+ * instruction being skipped. Actually, Line 2 TracePoint event
+ * is never fired in the following code:
+ *
+ * 1: raise if 1 == 2
+ * 2: while true
+ * 3: break
+ * 4: end
+ *
+ * This is critical for coverage measurement. [Bug #15980]
+ *
+ * This is a stopgap measure: stop the jump-jump optimization if
+ * coverage measurement is enabled and if the skipped instruction
+ * has any event flag.
+ *
+ * Note that, still, TracePoint Line event does not occur on Line 2.
+ * This should be fixed in future.
+ */
+ int stop_optimization =
+ ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq) &&
+ nobj->insn_info.events;
+ if (!stop_optimization) {
+ INSN *pobj = (INSN *)iobj->link.prev;
+ int prev_dup = 0;
+ if (pobj) {
+ if (!IS_INSN(&pobj->link))
+ pobj = 0;
+ else if (IS_INSN_ID(pobj, dup))
+ prev_dup = 1;
+ }
+
+ for (;;) {
+ if (IS_INSN(&nobj->link) && IS_INSN_ID(nobj, jump)) {
+ replace_destination(iobj, nobj);
+ }
+ else if (prev_dup && IS_INSN_ID(nobj, dup) &&
+ !!(nobj = (INSN *)nobj->link.next) &&
+ /* basic blocks, with no labels in the middle */
+ nobj->insn_id == iobj->insn_id) {
+ /*
+ * dup
+ * if L1
+ * ...
+ * L1:
+ * dup
+ * if L2
+ * =>
+ * dup
+ * if L2
+ * ...
+ * L1:
+ * dup
+ * if L2
+ */
+ replace_destination(iobj, nobj);
+ }
+ else if (pobj) {
+ /*
+ * putnil
+ * if L1
+ * =>
+ * # nothing
+ *
+ * putobject true
+ * if L1
+ * =>
+ * jump L1
+ *
+ * putstring ".."
+ * if L1
+ * =>
+ * jump L1
+ *
+ * putstring ".."
+ * dup
+ * if L1
+ * =>
+ * putstring ".."
+ * jump L1
+ *
+ */
+ int cond;
+ if (prev_dup && IS_INSN(pobj->link.prev)) {
+ pobj = (INSN *)pobj->link.prev;
+ }
+ if (IS_INSN_ID(pobj, putobject)) {
+ cond = (IS_INSN_ID(iobj, branchif) ?
+ OPERAND_AT(pobj, 0) != Qfalse :
+ IS_INSN_ID(iobj, branchunless) ?
+ OPERAND_AT(pobj, 0) == Qfalse :
+ FALSE);
+ }
+ else if (IS_INSN_ID(pobj, putstring) ||
+ IS_INSN_ID(pobj, duparray) ||
+ IS_INSN_ID(pobj, newarray)) {
+ cond = IS_INSN_ID(iobj, branchif);
+ }
+ else if (IS_INSN_ID(pobj, putnil)) {
+ cond = !IS_INSN_ID(iobj, branchif);
+ }
+ else break;
+ if (prev_dup || !IS_INSN_ID(pobj, newarray)) {
+ ELEM_REMOVE(iobj->link.prev);
+ }
+ else if (!iseq_pop_newarray(iseq, pobj)) {
+ pobj = new_insn_core(iseq, pobj->insn_info.line_no, BIN(pop), 0, NULL);
+ ELEM_INSERT_PREV(&iobj->link, &pobj->link);
+ }
+ if (cond) {
+ if (prev_dup) {
+ pobj = new_insn_core(iseq, pobj->insn_info.line_no, BIN(putnil), 0, NULL);
+ ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
+ }
+ iobj->insn_id = BIN(jump);
+ goto again;
+ }
+ else {
+ unref_destination(iobj, 0);
+ ELEM_REMOVE(&iobj->link);
+ }
+ break;
+ }
+ else break;
+ nobj = (INSN *)get_destination_insn(nobj);
+ }
+ }
}
if (IS_INSN_ID(iobj, pop)) {
@@ -5032,6 +5054,9 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int
branches);
end_label = NEW_LABEL(line);
ADD_INSNL(then_seq, line, jump, end_label);
+ if (!popped) {
+ ADD_INSN(then_seq, line, pop);
+ }
}
ADD_SEQ(ret, then_seq);
}
diff --git a/configure.ac b/configure.ac
index 2c4d2888d2..282cf4618f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -552,7 +552,19 @@ AS_IF([test "$GCC" = yes], [
# argument check. The performance drop is very little and Ubuntu enables
# _FORTIFY_SOURCE=2 by default. So, let's support it for protecting us from
# a mistake of silly C extensions.
- RUBY_TRY_CFLAGS(-D_FORTIFY_SOURCE=2, [RUBY_APPEND_OPTION(XCFLAGS, -D_FORTIFY_SOURCE=2)])
+
+ # TODO: check if link succeeds with _FORTIFY_SOURCE=2.
+ AS_CASE(["$target_os"],
+ [mingw*], [
+ fortify_source=no
+ ])
+ AC_ARG_ENABLE(fortify_source,
+ AS_HELP_STRING([--disable-fortify-source],
+ [disable -D_FORTIFY_SOURCE=2 option, which causes link error on mingw]),
+ [fortify_source=$enableval])
+ AS_IF([test "x$fortify_source" != xno], [
+ RUBY_TRY_CFLAGS(-D_FORTIFY_SOURCE=2, [RUBY_APPEND_OPTION(XCFLAGS, -D_FORTIFY_SOURCE=2)])
+ ])
: ${MJIT_HEADER_FLAGS='-P -dD'}
@@ -1418,7 +1430,7 @@ AS_IF([test "$GCC" = yes], [
])
AC_CACHE_CHECK([for __atomic builtins], [rb_cv_gcc_atomic_builtins], [
- AC_TRY_LINK([unsigned char atomic_var;],
+ AC_TRY_LINK([unsigned int atomic_var;],
[
__atomic_exchange_n(&atomic_var, 0, __ATOMIC_SEQ_CST);
__atomic_exchange_n(&atomic_var, 1, __ATOMIC_SEQ_CST);
@@ -1433,7 +1445,7 @@ AS_IF([test "$GCC" = yes], [
])
AC_CACHE_CHECK([for __sync builtins], [rb_cv_gcc_sync_builtins], [
- AC_TRY_LINK([unsigned char atomic_var;],
+ AC_TRY_LINK([unsigned int atomic_var;],
[
__sync_lock_test_and_set(&atomic_var, 0);
__sync_lock_test_and_set(&atomic_var, 1);
@@ -1828,10 +1840,15 @@ AC_CHECK_FUNCS(getgidx)
AC_CHECK_FUNCS(getgrnam)
AC_CHECK_FUNCS(getgrnam_r)
AC_CHECK_FUNCS(getgroups)
+AC_CHECK_FUNCS(getlogin)
+AC_CHECK_FUNCS(getlogin_r)
AC_CHECK_FUNCS(getpgid)
AC_CHECK_FUNCS(getpgrp)
AC_CHECK_FUNCS(getpriority)
+AC_CHECK_FUNCS(getpwnam)
AC_CHECK_FUNCS(getpwnam_r)
+AC_CHECK_FUNCS(getpwuid)
+AC_CHECK_FUNCS(getpwuid_r)
AC_CHECK_FUNCS(getresgid)
AC_CHECK_FUNCS(getresuid)
AC_CHECK_FUNCS(getrlimit)
@@ -2392,11 +2409,11 @@ AS_IF([test x"$enable_pthread" = xyes], [
AC_CACHE_CHECK([arguments of pthread_setname_np], [rb_cv_func_pthread_setname_np_arguments],
[rb_cv_func_pthread_setname_np_arguments=
# Linux,AIX, (pthread_self(), name)
- # NetBSD (pthread_self(), name, \"%s\")
+ # NetBSD (pthread_self(), \"%s\", name)
# Darwin (name)
for mac in \
"(pthread_self(), name)" \
- "(pthread_self(), name, \"%s\")" \
+ "(pthread_self(), \"%s\", name)" \
"(name)" \
; do
AC_TRY_COMPILE([
@@ -2841,12 +2858,12 @@ main(void)
stack_t ss;
struct sigaction sa;
- ss.ss_sp = malloc(SIGSTKSZ);
+ ss.ss_sp = malloc(16*1024);
if (ss.ss_sp == NULL) {
fprintf(stderr, "cannot allocate memory for sigaltstack\n");
return EXIT_FAILURE;
}
- ss.ss_size = SIGSTKSZ;
+ ss.ss_size = 16*1024;
ss.ss_flags = 0;
if (sigaltstack(&ss, NULL) == -1) {
fprintf(stderr, "sigaltstack failed\n");
diff --git a/doc/syntax/methods.rdoc b/doc/syntax/methods.rdoc
index a47c1a3cbf..5d070f8586 100644
--- a/doc/syntax/methods.rdoc
+++ b/doc/syntax/methods.rdoc
@@ -355,12 +355,23 @@ converted to an Array:
gather_arguments 1, 2, 3 # prints [1, 2, 3]
-The array argument must be the last positional argument, it must appear before
-any keyword arguments.
+The array argument must appear before any keyword arguments.
+
+It is possible to gather arguments at the beginning or in the middle:
+
+ def gather_arguments(first_arg, *middle_arguments, last_arg)
+ p middle_arguments
+ end
+
+ gather_arguments 1, 2, 3, 4 # prints [2, 3]
The array argument will capture a Hash as the last entry if a hash was sent by
the caller after all positional arguments.
+ def gather_arguments(*arguments)
+ p arguments
+ end
+
gather_arguments 1, a: 2 # prints [1, {:a=>2}]
However, this only occurs if the method does not declare any keyword arguments.
diff --git a/enumerator.c b/enumerator.c
index 6db3bbe50d..686dab0b20 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -2939,6 +2939,88 @@ arith_seq_first(int argc, VALUE *argv, VALUE self)
return rb_call_super(argc, argv);
}
+static inline VALUE
+num_plus(VALUE a, VALUE b)
+{
+ if (RB_INTEGER_TYPE_P(a)) {
+ return rb_int_plus(a, b);
+ }
+ else if (RB_FLOAT_TYPE_P(a)) {
+ return rb_float_plus(a, b);
+ }
+ else if (RB_TYPE_P(a, T_RATIONAL)) {
+ return rb_rational_plus(a, b);
+ }
+ else {
+ return rb_funcallv(a, '+', 1, &b);
+ }
+}
+
+static inline VALUE
+num_minus(VALUE a, VALUE b)
+{
+ if (RB_INTEGER_TYPE_P(a)) {
+ return rb_int_minus(a, b);
+ }
+ else if (RB_FLOAT_TYPE_P(a)) {
+ return rb_float_minus(a, b);
+ }
+ else if (RB_TYPE_P(a, T_RATIONAL)) {
+ return rb_rational_minus(a, b);
+ }
+ else {
+ return rb_funcallv(a, '-', 1, &b);
+ }
+}
+
+static inline VALUE
+num_mul(VALUE a, VALUE b)
+{
+ if (RB_INTEGER_TYPE_P(a)) {
+ return rb_int_mul(a, b);
+ }
+ else if (RB_FLOAT_TYPE_P(a)) {
+ return rb_float_mul(a, b);
+ }
+ else if (RB_TYPE_P(a, T_RATIONAL)) {
+ return rb_rational_mul(a, b);
+ }
+ else {
+ return rb_funcallv(a, '*', 1, &b);
+ }
+}
+
+static inline VALUE
+num_idiv(VALUE a, VALUE b)
+{
+ VALUE q;
+ if (RB_INTEGER_TYPE_P(a)) {
+ q = rb_int_idiv(a, b);
+ }
+ else if (RB_FLOAT_TYPE_P(a)) {
+ q = rb_float_div(a, b);
+ }
+ else if (RB_TYPE_P(a, T_RATIONAL)) {
+ q = rb_rational_div(a, b);
+ }
+ else {
+ q = rb_funcallv(a, '/', 1, &b);
+ }
+
+ if (RB_INTEGER_TYPE_P(q)) {
+ return q;
+ }
+ else if (RB_FLOAT_TYPE_P(q)) {
+ return rb_float_floor(q, 0);
+ }
+ else if (RB_TYPE_P(q, T_RATIONAL)) {
+ return rb_rational_floor(q, 0);
+ }
+ else {
+ return rb_funcall(q, rb_intern("floor"), 0);
+ }
+}
+
/*
* call-seq:
* aseq.last -> num or nil
@@ -2963,7 +3045,7 @@ arith_seq_last(int argc, VALUE *argv, VALUE self)
b = arith_seq_begin(self);
s = arith_seq_step(self);
- len_1 = rb_int_idiv(rb_int_minus(e, b), s);
+ len_1 = num_idiv(num_minus(e, b), s);
if (rb_num_negative_int_p(len_1)) {
if (argc == 0) {
return Qnil;
@@ -2971,9 +3053,9 @@ arith_seq_last(int argc, VALUE *argv, VALUE self)
return rb_ary_new_capa(0);
}
- last = rb_int_plus(b, rb_int_mul(s, len_1));
+ last = num_plus(b, num_mul(s, len_1));
if ((last_is_adjusted = arith_seq_exclude_end_p(self) && rb_equal(last, e))) {
- last = rb_int_minus(last, s);
+ last = num_minus(last, s);
}
if (argc == 0) {
@@ -3183,22 +3265,22 @@ arith_seq_each(VALUE self)
return self;
}
- len_1 = rb_int_idiv(rb_int_minus(e, c), s);
- last = rb_int_plus(c, rb_int_mul(s, len_1));
+ len_1 = num_idiv(num_minus(e, c), s);
+ last = num_plus(c, num_mul(s, len_1));
if (x && rb_equal(last, e)) {
- last = rb_int_minus(last, s);
+ last = num_minus(last, s);
}
if (rb_num_negative_int_p(s)) {
while (NUM_GE(c, last)) {
rb_yield(c);
- c = rb_int_plus(c, s);
+ c = num_plus(c, s);
}
}
else {
while (NUM_GE(last, c)) {
rb_yield(c);
- c = rb_int_plus(c, s);
+ c = num_plus(c, s);
}
}
diff --git a/eval.c b/eval.c
index 1eeaec56cb..47c9cac2b7 100644
--- a/eval.c
+++ b/eval.c
@@ -896,38 +896,20 @@ rb_need_block(void)
}
}
-/*! An equivalent of \c rescue clause.
- *
- * Equivalent to <code>begin .. rescue err_type .. end</code>
- *
- * \param[in] b_proc a function which potentially raises an exception.
- * \param[in] data1 the argument of \a b_proc
- * \param[in] r_proc a function which rescues an exception in \a b_proc.
- * \param[in] data2 the first argument of \a r_proc
- * \param[in] ... 1 or more exception classes. Must be terminated by \c (VALUE)0.
- *
- * First it calls the function \a b_proc, with \a data1 as the argument.
- * When \a b_proc raises an exception, it calls \a r_proc with \a data2 and
- * the exception object if the exception is a kind of one of the given
- * exception classes.
- *
- * \return the return value of \a b_proc if no exception occurs,
- * or the return value of \a r_proc if otherwise.
- * \sa rb_rescue
- * \sa rb_ensure
- * \sa rb_protect
- * \ingroup exception
+/*!
+ * \copydoc rb_rescue2
+ * \param[in] args exception classes, terminated by 0.
*/
-VALUE
-rb_rescue2(VALUE (* b_proc) (ANYARGS), VALUE data1,
- VALUE (* r_proc) (ANYARGS), VALUE data2, ...)
+static VALUE
+rb_vrescue2(VALUE (* b_proc) (VALUE), VALUE data1,
+ VALUE (* r_proc) (VALUE, VALUE), VALUE data2,
+ va_list args)
{
enum ruby_tag_type state;
rb_execution_context_t * volatile ec = GET_EC();
rb_control_frame_t *volatile cfp = ec->cfp;
volatile VALUE result = Qfalse;
volatile VALUE e_info = ec->errinfo;
- va_list args;
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
@@ -950,14 +932,12 @@ rb_rescue2(VALUE (* b_proc) (ANYARGS), VALUE data1,
int handle = FALSE;
VALUE eclass;
- va_init_list(args, data2);
while ((eclass = va_arg(args, VALUE)) != 0) {
if (rb_obj_is_kind_of(ec->errinfo, eclass)) {
handle = TRUE;
break;
}
}
- va_end(args);
if (handle) {
result = Qnil;
@@ -978,6 +958,38 @@ rb_rescue2(VALUE (* b_proc) (ANYARGS), VALUE data1,
/*! An equivalent of \c rescue clause.
*
+ * Equivalent to <code>begin .. rescue err_type .. end</code>
+ *
+ * \param[in] b_proc a function which potentially raises an exception.
+ * \param[in] data1 the argument of \a b_proc
+ * \param[in] r_proc a function which rescues an exception in \a b_proc.
+ * \param[in] data2 the first argument of \a r_proc
+ * \param[in] ... 1 or more exception classes. Must be terminated by \c (VALUE)0.
+ *
+ * First it calls the function \a b_proc, with \a data1 as the argument.
+ * When \a b_proc raises an exception, it calls \a r_proc with \a data2 and
+ * the exception object if the exception is a kind of one of the given
+ * exception classes.
+ *
+ * \return the return value of \a b_proc if no exception occurs,
+ * or the return value of \a r_proc if otherwise.
+ * \sa rb_rescue
+ * \sa rb_ensure
+ * \sa rb_protect
+ * \ingroup exception
+ */
+VALUE
+rb_rescue2(VALUE (* b_proc) (ANYARGS), VALUE data1,
+ VALUE (* r_proc) (ANYARGS), VALUE data2, ...)
+{
+ va_list ap;
+ va_start(ap, data2);
+ return rb_vrescue2((VALUE (*)(VALUE))b_proc, data1, (VALUE (*)(VALUE, VALUE))r_proc, data2, ap);
+ va_end(ap);
+}
+
+/*! An equivalent of \c rescue clause.
+ *
* Equivalent to <code>begin .. rescue .. end</code>.
*
* It is same as
diff --git a/eval_error.c b/eval_error.c
index 8ea58a4742..c2d33ef498 100644
--- a/eval_error.c
+++ b/eval_error.c
@@ -308,6 +308,7 @@ rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
volatile uint8_t raised_flag = ec->raised_flag;
volatile VALUE errat = Qundef;
volatile VALUE emesg = Qundef;
+ volatile bool written = false;
if (NIL_P(errinfo))
return;
@@ -322,7 +323,10 @@ rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
emesg = rb_get_message(errinfo);
}
- rb_error_write(errinfo, emesg, errat, Qnil, Qnil, Qnil);
+ if (!written) {
+ written = true;
+ rb_error_write(errinfo, emesg, errat, Qnil, Qnil, Qfalse);
+ }
EC_POP_TAG();
ec->errinfo = errinfo;
diff --git a/ext/date/date.gemspec b/ext/date/date.gemspec
index ddb9608f4e..b30a055746 100644
--- a/ext/date/date.gemspec
+++ b/ext/date/date.gemspec
@@ -1,7 +1,12 @@
# frozen_string_literal: true
+
+version = File.foreach(File.expand_path("../lib/date.rb", __FILE__)).find do |line|
+ /^\s*VERSION\s*=\s*["'](.*)["']/ =~ line and break $1
+end
+
Gem::Specification.new do |s|
s.name = "date"
- s.version = '2.0.0'
+ s.version = version
s.summary = "A subclass of Object includes Comparable module for handling dates."
s.description = "A subclass of Object includes Comparable module for handling dates."
diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index 67ed6171a7..1734ec0349 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -3692,7 +3692,7 @@ rt_rewrite_frags(VALUE hash)
{
VALUE seconds;
- seconds = del_hash("seconds");
+ seconds = ref_hash("seconds");
if (!NIL_P(seconds)) {
VALUE offset, d, h, min, s, fr;
@@ -3717,6 +3717,7 @@ rt_rewrite_frags(VALUE hash)
set_hash("min", min);
set_hash("sec", s);
set_hash("sec_fraction", fr);
+ del_hash("seconds");
}
return hash;
}
@@ -4291,12 +4292,40 @@ date_s_strptime(int argc, VALUE *argv, VALUE klass)
VALUE date__parse(VALUE str, VALUE comp);
+static size_t
+get_limit(VALUE opt)
+{
+ if (!NIL_P(opt)) {
+ VALUE limit = rb_hash_aref(opt, ID2SYM(rb_intern("limit")));
+ if (NIL_P(limit)) return SIZE_MAX;
+ return NUM2SIZET(limit);
+ }
+ return 128;
+}
+
+static void
+check_limit(VALUE str, VALUE opt)
+{
+ if (NIL_P(str)) return;
+ if (SYMBOL_P(str)) str = rb_sym2str(str);
+
+ StringValue(str);
+ size_t slen = RSTRING_LEN(str);
+ size_t limit = get_limit(opt);
+ if (slen > limit) {
+ rb_raise(rb_eArgError,
+ "string length (%"PRI_SIZE_PREFIX"u) exceeds the limit %"PRI_SIZE_PREFIX"u", slen, limit);
+ }
+}
+
static VALUE
date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
{
- VALUE vstr, vcomp, hash;
+ VALUE vstr, vcomp, hash, opt;
- rb_scan_args(argc, argv, "11", &vstr, &vcomp);
+ rb_scan_args(argc, argv, "11:", &vstr, &vcomp, &opt);
+ if (!NIL_P(opt)) argc--;
+ check_limit(vstr, opt);
StringValue(vstr);
if (!rb_enc_str_asciicompat_p(vstr))
rb_raise(rb_eArgError,
@@ -4306,12 +4335,22 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
hash = date__parse(vstr, vcomp);
+ {
+ VALUE zone = ref_hash("zone");
+
+ if (!NIL_P(zone)) {
+ rb_enc_copy(zone, vstr);
+ OBJ_INFECT(zone, vstr);
+ set_hash("zone", zone);
+ }
+ }
+
return hash;
}
/*
* call-seq:
- * Date._parse(string[, comp=true]) -> hash
+ * Date._parse(string[, comp=true], limit: 128) -> hash
*
* Parses the given representation of date and time, and returns a
* hash of parsed elements. This method does not function as a
@@ -4322,6 +4361,10 @@ date_s__parse_internal(int argc, VALUE *argv, VALUE klass)
* it full.
*
* Date._parse('2001-02-03') #=> {:year=>2001, :mon=>2, :mday=>3}
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s__parse(int argc, VALUE *argv, VALUE klass)
@@ -4331,7 +4374,7 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * Date.parse(string='-4712-01-01'[, comp=true[, start=Date::ITALY]]) -> date
+ * Date.parse(string='-4712-01-01'[, comp=true[, start=Date::ITALY]], limit: 128) -> date
*
* Parses the given representation of date and time, and creates a
* date object. This method does not function as a validator.
@@ -4343,13 +4386,18 @@ date_s__parse(int argc, VALUE *argv, VALUE klass)
* Date.parse('2001-02-03') #=> #<Date: 2001-02-03 ...>
* Date.parse('20010203') #=> #<Date: 2001-02-03 ...>
* Date.parse('3rd Feb 2001') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_parse(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, comp, sg;
+ VALUE str, comp, sg, opt;
- rb_scan_args(argc, argv, "03", &str, &comp, &sg);
+ rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4361,11 +4409,12 @@ date_s_parse(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE argv2[2], hash;
-
- argv2[0] = str;
- argv2[1] = comp;
- hash = date_s__parse(2, argv2, klass);
+ int argc2 = 2;
+ VALUE argv2[3];
+ argv2[0] = str;
+ argv2[1] = comp;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__parse(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
@@ -4379,19 +4428,28 @@ VALUE date__jisx0301(VALUE);
/*
* call-seq:
- * Date._iso8601(string) -> hash
+ * Date._iso8601(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__iso8601(VALUE klass, VALUE str)
+date_s__iso8601(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__iso8601(str);
}
/*
* call-seq:
- * Date.iso8601(string='-4712-01-01'[, start=Date::ITALY]) -> date
+ * Date.iso8601(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical ISO 8601 formats.
@@ -4399,13 +4457,18 @@ date_s__iso8601(VALUE klass, VALUE str)
* Date.iso8601('2001-02-03') #=> #<Date: 2001-02-03 ...>
* Date.iso8601('20010203') #=> #<Date: 2001-02-03 ...>
* Date.iso8601('2001-W05-6') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_iso8601(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4415,38 +4478,56 @@ date_s_iso8601(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__iso8601(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__iso8601(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._rfc3339(string) -> hash
+ * Date._rfc3339(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__rfc3339(VALUE klass, VALUE str)
+date_s__rfc3339(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__rfc3339(str);
}
/*
* call-seq:
- * Date.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> date
+ * Date.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical RFC 3339 formats.
*
* Date.rfc3339('2001-02-03T04:05:06+07:00') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_rfc3339(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4456,38 +4537,56 @@ date_s_rfc3339(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc3339(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__rfc3339(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._xmlschema(string) -> hash
+ * Date._xmlschema(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__xmlschema(VALUE klass, VALUE str)
+date_s__xmlschema(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__xmlschema(str);
}
/*
* call-seq:
- * Date.xmlschema(string='-4712-01-01'[, start=Date::ITALY]) -> date
+ * Date.xmlschema(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical XML Schema formats.
*
* Date.xmlschema('2001-02-03') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4497,41 +4596,58 @@ date_s_xmlschema(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__xmlschema(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__xmlschema(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._rfc2822(string) -> hash
- * Date._rfc822(string) -> hash
+ * Date._rfc2822(string, limit: 128) -> hash
+ * Date._rfc822(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__rfc2822(VALUE klass, VALUE str)
+date_s__rfc2822(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__rfc2822(str);
}
/*
* call-seq:
- * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> date
- * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> date
+ * Date.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> date
+ * Date.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical RFC 2822 formats.
*
* Date.rfc2822('Sat, 3 Feb 2001 00:00:00 +0000')
* #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
switch (argc) {
case 0:
@@ -4541,39 +4657,56 @@ date_s_rfc2822(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc2822(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__rfc2822(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._httpdate(string) -> hash
+ * Date._httpdate(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__httpdate(VALUE klass, VALUE str)
+date_s__httpdate(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__httpdate(str);
}
/*
* call-seq:
- * Date.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY]) -> date
+ * Date.httpdate(string='Mon, 01 Jan -4712 00:00:00 GMT'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some RFC 2616 format.
*
* Date.httpdate('Sat, 03 Feb 2001 00:00:00 GMT')
* #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_httpdate(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
switch (argc) {
case 0:
@@ -4583,26 +4716,39 @@ date_s_httpdate(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__httpdate(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__httpdate(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * Date._jisx0301(string) -> hash
+ * Date._jisx0301(string, limit: 128) -> hash
*
* Returns a hash of parsed elements.
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
-date_s__jisx0301(VALUE klass, VALUE str)
+date_s__jisx0301(int argc, VALUE *argv, VALUE klass)
{
+ VALUE str, opt;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ check_limit(str, opt);
+
return date__jisx0301(str);
}
/*
* call-seq:
- * Date.jisx0301(string='-4712-01-01'[, start=Date::ITALY]) -> date
+ * Date.jisx0301(string='-4712-01-01'[, start=Date::ITALY], limit: 128) -> date
*
* Creates a new Date object by parsing from a string according to
* some typical JIS X 0301 formats.
@@ -4612,13 +4758,18 @@ date_s__jisx0301(VALUE klass, VALUE str)
* For no-era year, legacy format, Heisei is assumed.
*
* Date.jisx0301('13.02.03') #=> #<Date: 2001-02-03 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
date_s_jisx0301(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -4628,7 +4779,11 @@ date_s_jisx0301(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__jisx0301(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ if (!NIL_P(opt)) argv2[argc2++] = opt;
+ VALUE hash = date_s__jisx0301(argc2, argv2, klass);
return d_new_by_frags(klass, hash, sg);
}
}
@@ -7948,7 +8103,7 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
/*
* call-seq:
- * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]]) -> datetime
+ * DateTime.parse(string='-4712-01-01T00:00:00+00:00'[, comp=true[, start=Date::ITALY]], limit: 128) -> datetime
*
* Parses the given representation of date and time, and creates a
* DateTime object. This method does not function as a validator.
@@ -7962,13 +8117,18 @@ datetime_s_strptime(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
* DateTime.parse('3rd Feb 2001 04:05:06 PM')
* #=> #<DateTime: 2001-02-03T16:05:06+00:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_parse(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, comp, sg;
+ VALUE str, comp, sg, opt;
- rb_scan_args(argc, argv, "03", &str, &comp, &sg);
+ rb_scan_args(argc, argv, "03:", &str, &comp, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -7980,18 +8140,20 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE argv2[2], hash;
-
- argv2[0] = str;
- argv2[1] = comp;
- hash = date_s__parse(2, argv2, klass);
+ int argc2 = 2;
+ VALUE argv2[3];
+ argv2[0] = str;
+ argv2[1] = comp;
+ argv2[2] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__parse(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime
+ * DateTime.iso8601(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical ISO 8601 formats.
@@ -8002,13 +8164,18 @@ datetime_s_parse(int argc, VALUE *argv, VALUE klass)
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
* DateTime.iso8601('2001-W05-6T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_iso8601(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8018,27 +8185,37 @@ datetime_s_iso8601(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__iso8601(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2--;
+ VALUE hash = date_s__iso8601(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime
+ * DateTime.rfc3339(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical RFC 3339 formats.
*
* DateTime.rfc3339('2001-02-03T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8048,27 +8225,37 @@ datetime_s_rfc3339(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc3339(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__rfc3339(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime
+ * DateTime.xmlschema(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical XML Schema formats.
*
* DateTime.xmlschema('2001-02-03T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8078,28 +8265,38 @@ datetime_s_xmlschema(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__xmlschema(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__xmlschema(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> datetime
- * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY]) -> datetime
+ * DateTime.rfc2822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime
+ * DateTime.rfc822(string='Mon, 1 Jan -4712 00:00:00 +0000'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical RFC 2822 formats.
*
* DateTime.rfc2822('Sat, 3 Feb 2001 04:05:06 +0700')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8109,7 +8306,12 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__rfc2822(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__rfc2822(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
@@ -8123,13 +8325,18 @@ datetime_s_rfc2822(int argc, VALUE *argv, VALUE klass)
*
* DateTime.httpdate('Sat, 03 Feb 2001 04:05:06 GMT')
* #=> #<DateTime: 2001-02-03T04:05:06+00:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8139,14 +8346,19 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__httpdate(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__httpdate(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
/*
* call-seq:
- * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY]) -> datetime
+ * DateTime.jisx0301(string='-4712-01-01T00:00:00+00:00'[, start=Date::ITALY], limit: 128) -> datetime
*
* Creates a new DateTime object by parsing from a string according to
* some typical JIS X 0301 formats.
@@ -8158,13 +8370,18 @@ datetime_s_httpdate(int argc, VALUE *argv, VALUE klass)
*
* DateTime.jisx0301('13.02.03T04:05:06+07:00')
* #=> #<DateTime: 2001-02-03T04:05:06+07:00 ...>
+ *
+ * Raise an ArgumentError when the string length is longer than _limit_.
+ * You can stop this check by passing `limit: nil`, but note that
+ * it may take a long time to parse.
*/
static VALUE
datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass)
{
- VALUE str, sg;
+ VALUE str, sg, opt;
- rb_scan_args(argc, argv, "02", &str, &sg);
+ rb_scan_args(argc, argv, "02:", &str, &sg, &opt);
+ if (!NIL_P(opt)) argc--;
switch (argc) {
case 0:
@@ -8174,7 +8391,12 @@ datetime_s_jisx0301(int argc, VALUE *argv, VALUE klass)
}
{
- VALUE hash = date_s__jisx0301(klass, str);
+ int argc2 = 1;
+ VALUE argv2[2];
+ argv2[0] = str;
+ argv2[1] = opt;
+ if (!NIL_P(opt)) argc2++;
+ VALUE hash = date_s__jisx0301(argc2, argv2, klass);
return dt_new_by_frags(klass, hash, sg);
}
}
@@ -9325,19 +9547,19 @@ Init_date_core(void)
rb_define_singleton_method(cDate, "strptime", date_s_strptime, -1);
rb_define_singleton_method(cDate, "_parse", date_s__parse, -1);
rb_define_singleton_method(cDate, "parse", date_s_parse, -1);
- rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, 1);
+ rb_define_singleton_method(cDate, "_iso8601", date_s__iso8601, -1);
rb_define_singleton_method(cDate, "iso8601", date_s_iso8601, -1);
- rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, 1);
+ rb_define_singleton_method(cDate, "_rfc3339", date_s__rfc3339, -1);
rb_define_singleton_method(cDate, "rfc3339", date_s_rfc3339, -1);
- rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, 1);
+ rb_define_singleton_method(cDate, "_xmlschema", date_s__xmlschema, -1);
rb_define_singleton_method(cDate, "xmlschema", date_s_xmlschema, -1);
- rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, 1);
- rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, 1);
+ rb_define_singleton_method(cDate, "_rfc2822", date_s__rfc2822, -1);
+ rb_define_singleton_method(cDate, "_rfc822", date_s__rfc2822, -1);
rb_define_singleton_method(cDate, "rfc2822", date_s_rfc2822, -1);
rb_define_singleton_method(cDate, "rfc822", date_s_rfc2822, -1);
- rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, 1);
+ rb_define_singleton_method(cDate, "_httpdate", date_s__httpdate, -1);
rb_define_singleton_method(cDate, "httpdate", date_s_httpdate, -1);
- rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, 1);
+ rb_define_singleton_method(cDate, "_jisx0301", date_s__jisx0301, -1);
rb_define_singleton_method(cDate, "jisx0301", date_s_jisx0301, -1);
rb_define_method(cDate, "initialize", date_initialize, -1);
diff --git a/ext/date/date_parse.c b/ext/date/date_parse.c
index e0634c39f1..f06c07bae4 100644
--- a/ext/date/date_parse.c
+++ b/ext/date/date_parse.c
@@ -1863,26 +1863,30 @@ parse_ddd_cb(VALUE m, VALUE hash)
set_hash("zone", s5);
if (*cs5 == '[') {
- const char *s1, *s2;
+ VALUE vbuf = 0;
+ char *buf = ALLOCV_N(char, vbuf, l5 + 1);
+ char *s1, *s2, *s3;
VALUE zone;
- l5 -= 2;
- s1 = cs5 + 1;
- s2 = memchr(s1, ':', l5);
+ memcpy(buf, cs5, l5);
+ buf[l5 - 1] = '\0';
+
+ s1 = buf + 1;
+ s2 = strchr(buf, ':');
if (s2) {
+ *s2 = '\0';
s2++;
- zone = rb_str_subseq(s5, s2 - cs5, l5 - (s2 - s1));
- s5 = rb_str_subseq(s5, 1, s2 - s1);
}
- else {
- zone = rb_str_subseq(s5, 1, l5);
- if (isdigit((unsigned char)*s1))
- s5 = rb_str_append(rb_str_new_cstr("+"), zone);
- else
- s5 = zone;
- }
+ if (s2)
+ s3 = s2;
+ else
+ s3 = s1;
+ zone = rb_str_new2(s3);
set_hash("zone", zone);
- set_hash("offset", date_zone_to_diff(s5));
+ if (isdigit((unsigned char)*s1))
+ *--s1 = '+';
+ set_hash("offset", date_zone_to_diff(rb_str_new2(s1)));
+ ALLOCV_END(vbuf);
}
RB_GC_GUARD(s5);
}
@@ -2175,7 +2179,7 @@ date__parse(VALUE str, VALUE comp)
#endif
{
- if (RTEST(del_hash("_bc"))) {
+ if (RTEST(ref_hash("_bc"))) {
VALUE y;
y = ref_hash("cwyear");
@@ -2190,7 +2194,7 @@ date__parse(VALUE str, VALUE comp)
}
}
- if (RTEST(del_hash("_comp"))) {
+ if (RTEST(ref_hash("_comp"))) {
VALUE y;
y = ref_hash("cwyear");
@@ -2213,6 +2217,9 @@ date__parse(VALUE str, VALUE comp)
}
+ del_hash("_bc");
+ del_hash("_comp");
+
{
VALUE zone = ref_hash("zone");
if (!NIL_P(zone) && NIL_P(ref_hash("offset")))
diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c
index 26d9fd11bf..4383eb6fa1 100644
--- a/ext/date/date_strptime.c
+++ b/ext/date/date_strptime.c
@@ -669,7 +669,7 @@ date__strptime(const char *str, size_t slen,
if (fail_p())
return Qnil;
- cent = del_hash("_cent");
+ cent = ref_hash("_cent");
if (!NIL_P(cent)) {
VALUE year;
@@ -679,9 +679,10 @@ date__strptime(const char *str, size_t slen,
year = ref_hash("year");
if (!NIL_P(year))
set_hash("year", f_add(year, f_mul(cent, INT2FIX(100))));
+ del_hash("_cent");
}
- merid = del_hash("_merid");
+ merid = ref_hash("_merid");
if (!NIL_P(merid)) {
VALUE hour;
@@ -690,6 +691,7 @@ date__strptime(const char *str, size_t slen,
hour = f_mod(hour, INT2FIX(12));
set_hash("hour", f_add(hour, merid));
}
+ del_hash("_merid");
}
return hash;
diff --git a/ext/date/lib/date.rb b/ext/date/lib/date.rb
index 9170a99ae9..cea63b1259 100644
--- a/ext/date/lib/date.rb
+++ b/ext/date/lib/date.rb
@@ -4,6 +4,7 @@
require 'date_core'
class Date
+ VERSION = '2.0.3' # :nodoc:
class Infinity < Numeric # :nodoc:
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c
index 1a80b2b02a..2044c836ea 100644
--- a/ext/fiddle/closure.c
+++ b/ext/fiddle/closure.c
@@ -14,10 +14,7 @@ typedef struct {
} fiddle_closure;
#if defined(USE_FFI_CLOSURE_ALLOC)
-#elif defined(__OpenBSD__) || defined(__APPLE__) || defined(__linux__)
-# define USE_FFI_CLOSURE_ALLOC 0
-#elif defined(RUBY_LIBFFI_MODVERSION) && RUBY_LIBFFI_MODVERSION < 3000005 && \
- (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64))
+#elif !defined(HAVE_FFI_CLOSURE_ALLOC)
# define USE_FFI_CLOSURE_ALLOC 0
#else
# define USE_FFI_CLOSURE_ALLOC 1
diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb
index fce0cb4079..f8a94d41e7 100644
--- a/ext/fiddle/extconf.rb
+++ b/ext/fiddle/extconf.rb
@@ -13,7 +13,7 @@ if ! bundle
if have_header(ffi_header = 'ffi.h')
true
elsif have_header(ffi_header = 'ffi/ffi.h')
- $defs.push(format('-DUSE_HEADER_HACKS'))
+ $defs.push('-DUSE_HEADER_HACKS')
true
end and (have_library('ffi') || have_library('libffi'))
end or
@@ -114,8 +114,17 @@ end
if ver
ver = ver.gsub(/-rc\d+/, '') # If ver contains rc version, just ignored.
- ver = (ver.split('.') + [0,0])[0,3]
+ ver = (ver.split('.').map(&:to_i) + [0,0])[0,3]
$defs.push(%{-DRUBY_LIBFFI_MODVERSION=#{ '%d%03d%03d' % ver }})
+ warn "libffi_version: #{ver.join('.')}"
+end
+
+case
+when $mswin, $mingw, (ver && (ver <=> [3, 2]) >= 0)
+ $defs << "-DUSE_FFI_CLOSURE_ALLOC=1"
+when (ver && (ver <=> [3, 2]) < 0)
+else
+ have_func('ffi_closure_alloc', ffi_header)
end
have_header 'sys/mman.h'
@@ -142,7 +151,7 @@ types.each do |type, signed|
if /^\#define\s+SIZEOF_#{type}\s+(SIZEOF_(.+)|\d+)/ =~ config
if size = $2 and size != 'VOIDP'
size = types.fetch(size) {size}
- $defs << format("-DTYPE_%s=TYPE_%s", signed||type, size)
+ $defs << "-DTYPE_#{signed||type}=TYPE_#{size}"
end
if signed
check_signedness(type.downcase, "stddef.h")
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c
index 1ac69af844..a811441320 100644
--- a/ext/objspace/objspace.c
+++ b/ext/objspace/objspace.c
@@ -617,7 +617,7 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self)
VALUE hash = setup_hash(argc, argv);
if (imemo_type_ids[0] == 0) {
- imemo_type_ids[0] = rb_intern("imemo_none");
+ imemo_type_ids[0] = rb_intern("imemo_env");
imemo_type_ids[1] = rb_intern("imemo_cref");
imemo_type_ids[2] = rb_intern("imemo_svar");
imemo_type_ids[3] = rb_intern("imemo_throw_data");
@@ -626,7 +626,8 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self)
imemo_type_ids[6] = rb_intern("imemo_ment");
imemo_type_ids[7] = rb_intern("imemo_iseq");
imemo_type_ids[8] = rb_intern("imemo_tmpbuf");
- imemo_type_ids[9] = rb_intern("imemo_parser_strterm");
+ imemo_type_ids[9] = rb_intern("imemo_ast");
+ imemo_type_ids[10] = rb_intern("imemo_parser_strterm");
}
rb_objspace_each_objects(count_imemo_objects_i, (void *)hash);
diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c
index c46d3937c3..8c647e8922 100644
--- a/ext/win32ole/win32ole.c
+++ b/ext/win32ole/win32ole.c
@@ -2663,7 +2663,7 @@ ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket)
/*------------------------------------------
hash object ==> named dispatch parameters
--------------------------------------------*/
- cNamedArgs = rb_long2int(RHASH_SIZE(param));
+ cNamedArgs = rb_long2int((long)RHASH_SIZE(param));
op.dp.cArgs = cNamedArgs + argc - 2;
op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1);
op.dp.rgvarg = ALLOCA_N(VARIANTARG, op.dp.cArgs);
@@ -3974,6 +3974,7 @@ check_nano_server(void)
}
}
+LCID cWIN32OLE_lcid;
void
Init_win32ole(void)
diff --git a/ext/win32ole/win32ole.h b/ext/win32ole/win32ole.h
index c019930397..cd627ef765 100644
--- a/ext/win32ole/win32ole.h
+++ b/ext/win32ole/win32ole.h
@@ -112,8 +112,8 @@ struct oledata {
IDispatch *pDispatch;
};
-VALUE cWIN32OLE;
-LCID cWIN32OLE_lcid;
+extern VALUE cWIN32OLE;
+extern LCID cWIN32OLE_lcid;
struct oledata *oledata_get_struct(VALUE obj);
LPWSTR ole_vstr2wc(VALUE vstr);
diff --git a/ext/win32ole/win32ole_error.c b/ext/win32ole/win32ole_error.c
index 022527617e..2bb5156263 100644
--- a/ext/win32ole/win32ole_error.c
+++ b/ext/win32ole/win32ole_error.c
@@ -60,6 +60,9 @@ ole_raise(HRESULT hr, VALUE ecs, const char *fmt, ...)
rb_exc_raise(rb_exc_new_str(ecs, msg));
}
+VALUE eWIN32OLERuntimeError;
+VALUE eWIN32OLEQueryInterfaceError;
+
void
Init_win32ole_error(void)
{
diff --git a/ext/win32ole/win32ole_error.h b/ext/win32ole/win32ole_error.h
index 296eb101ad..a2f329856f 100644
--- a/ext/win32ole/win32ole_error.h
+++ b/ext/win32ole/win32ole_error.h
@@ -1,8 +1,8 @@
#ifndef WIN32OLE_ERROR_H
#define WIN32OLE_ERROR_H 1
-VALUE eWIN32OLERuntimeError;
-VALUE eWIN32OLEQueryInterfaceError;
+extern VALUE eWIN32OLERuntimeError;
+extern VALUE eWIN32OLEQueryInterfaceError;
NORETURN(PRINTF_ARGS(void ole_raise(HRESULT hr, VALUE ecs, const char *fmt, ...), 3, 4));
void Init_win32ole_error(void);
diff --git a/ext/win32ole/win32ole_method.c b/ext/win32ole/win32ole_method.c
index ffa9324657..bf668300c2 100644
--- a/ext/win32ole/win32ole_method.c
+++ b/ext/win32ole/win32ole_method.c
@@ -923,6 +923,8 @@ folemethod_inspect(VALUE self)
return default_inspect(self, "WIN32OLE_METHOD");
}
+VALUE cWIN32OLE_METHOD;
+
void Init_win32ole_method(void)
{
cWIN32OLE_METHOD = rb_define_class("WIN32OLE_METHOD", rb_cObject);
diff --git a/ext/win32ole/win32ole_method.h b/ext/win32ole/win32ole_method.h
index ff2898ebeb..ef907d2fac 100644
--- a/ext/win32ole/win32ole_method.h
+++ b/ext/win32ole/win32ole_method.h
@@ -7,7 +7,7 @@ struct olemethoddata {
UINT index;
};
-VALUE cWIN32OLE_METHOD;
+extern VALUE cWIN32OLE_METHOD;
VALUE folemethod_s_allocate(VALUE klass);
VALUE ole_methods_from_typeinfo(ITypeInfo *pTypeInfo, int mask);
VALUE create_win32ole_method(ITypeInfo *pTypeInfo, VALUE name);
diff --git a/ext/win32ole/win32ole_record.c b/ext/win32ole/win32ole_record.c
index e8838832a7..03523bc47d 100644
--- a/ext/win32ole/win32ole_record.c
+++ b/ext/win32ole/win32ole_record.c
@@ -589,6 +589,8 @@ folerecord_inspect(VALUE self)
field);
}
+VALUE cWIN32OLE_RECORD;
+
void
Init_win32ole_record(void)
{
diff --git a/ext/win32ole/win32ole_record.h b/ext/win32ole/win32ole_record.h
index ea431e91f7..ab1df0ee7f 100644
--- a/ext/win32ole/win32ole_record.h
+++ b/ext/win32ole/win32ole_record.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_RECORD_H
#define WIN32OLE_RECORD_H 1
-VALUE cWIN32OLE_RECORD;
+extern VALUE cWIN32OLE_RECORD;
void ole_rec2variant(VALUE rec, VARIANT *var);
void olerecord_set_ivar(VALUE obj, IRecordInfo *pri, void *prec);
VALUE create_win32ole_record(IRecordInfo *pri, void *prec);
diff --git a/ext/win32ole/win32ole_type.c b/ext/win32ole/win32ole_type.c
index e6ac402ecf..fa39bf3696 100644
--- a/ext/win32ole/win32ole_type.c
+++ b/ext/win32ole/win32ole_type.c
@@ -883,6 +883,8 @@ foletype_inspect(VALUE self)
return default_inspect(self, "WIN32OLE_TYPE");
}
+VALUE cWIN32OLE_TYPE;
+
void Init_win32ole_type(void)
{
cWIN32OLE_TYPE = rb_define_class("WIN32OLE_TYPE", rb_cObject);
diff --git a/ext/win32ole/win32ole_type.h b/ext/win32ole/win32ole_type.h
index a26bf3e043..87b551e502 100644
--- a/ext/win32ole/win32ole_type.h
+++ b/ext/win32ole/win32ole_type.h
@@ -1,6 +1,6 @@
#ifndef WIN32OLE_TYPE_H
#define WIN32OLE_TYPE_H 1
-VALUE cWIN32OLE_TYPE;
+extern VALUE cWIN32OLE_TYPE;
VALUE create_win32ole_type(ITypeInfo *pTypeInfo, VALUE name);
ITypeInfo *itypeinfo(VALUE self);
VALUE ole_type_from_itypeinfo(ITypeInfo *pTypeInfo);
diff --git a/ext/win32ole/win32ole_typelib.c b/ext/win32ole/win32ole_typelib.c
index 35376c644b..d89f181e07 100644
--- a/ext/win32ole/win32ole_typelib.c
+++ b/ext/win32ole/win32ole_typelib.c
@@ -822,6 +822,8 @@ foletypelib_inspect(VALUE self)
return default_inspect(self, "WIN32OLE_TYPELIB");
}
+VALUE cWIN32OLE_TYPELIB;
+
void
Init_win32ole_typelib(void)
{
diff --git a/ext/win32ole/win32ole_typelib.h b/ext/win32ole/win32ole_typelib.h
index 9fc117fcb4..2c2730bb58 100644
--- a/ext/win32ole/win32ole_typelib.h
+++ b/ext/win32ole/win32ole_typelib.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_TYPELIB_H
#define WIN32OLE_TYPELIB_H 1
-VALUE cWIN32OLE_TYPELIB;
+extern VALUE cWIN32OLE_TYPELIB;
void Init_win32ole_typelib(void);
ITypeLib * itypelib(VALUE self);
diff --git a/ext/win32ole/win32ole_variable.c b/ext/win32ole/win32ole_variable.c
index 3dc9972ee7..803083156c 100644
--- a/ext/win32ole/win32ole_variable.c
+++ b/ext/win32ole/win32ole_variable.c
@@ -365,6 +365,8 @@ folevariable_inspect(VALUE self)
return make_inspect("WIN32OLE_VARIABLE", detail);
}
+VALUE cWIN32OLE_VARIABLE;
+
void Init_win32ole_variable(void)
{
cWIN32OLE_VARIABLE = rb_define_class("WIN32OLE_VARIABLE", rb_cObject);
diff --git a/ext/win32ole/win32ole_variable.h b/ext/win32ole/win32ole_variable.h
index 704dc13508..209613fd44 100644
--- a/ext/win32ole/win32ole_variable.h
+++ b/ext/win32ole/win32ole_variable.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_VARIABLE_H
#define WIN32OLE_VARIABLE_H 1
-VALUE cWIN32OLE_VARIABLE;
+extern VALUE cWIN32OLE_VARIABLE;
VALUE create_win32ole_variable(ITypeInfo *pTypeInfo, UINT index, VALUE name);
void Init_win32ole_variable(void);
diff --git a/ext/win32ole/win32ole_variant.c b/ext/win32ole/win32ole_variant.c
index 3ff8f4ffbf..93f0636593 100644
--- a/ext/win32ole/win32ole_variant.c
+++ b/ext/win32ole/win32ole_variant.c
@@ -689,6 +689,8 @@ ole_variant2variant(VALUE val, VARIANT *var)
VariantCopy(var, &(pvar->var));
}
+VALUE cWIN32OLE_VARIANT;
+
void
Init_win32ole_variant(void)
{
diff --git a/ext/win32ole/win32ole_variant.h b/ext/win32ole/win32ole_variant.h
index efe7ea8bef..4bd3b0aeea 100644
--- a/ext/win32ole/win32ole_variant.h
+++ b/ext/win32ole/win32ole_variant.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_VARIANT_H
#define WIN32OLE_VARIANT_H 1
-VALUE cWIN32OLE_VARIANT;
+extern VALUE cWIN32OLE_VARIANT;
void ole_variant2variant(VALUE val, VARIANT *var);
void Init_win32ole_variant(void);
diff --git a/ext/win32ole/win32ole_variant_m.c b/ext/win32ole/win32ole_variant_m.c
index 4d76fdc790..145c08a16e 100644
--- a/ext/win32ole/win32ole_variant_m.c
+++ b/ext/win32ole/win32ole_variant_m.c
@@ -1,5 +1,7 @@
#include "win32ole.h"
+VALUE mWIN32OLE_VARIANT;
+
void Init_win32ole_variant_m(void)
{
/*
diff --git a/ext/win32ole/win32ole_variant_m.h b/ext/win32ole/win32ole_variant_m.h
index afbef30218..6272a6578f 100644
--- a/ext/win32ole/win32ole_variant_m.h
+++ b/ext/win32ole/win32ole_variant_m.h
@@ -1,7 +1,7 @@
#ifndef WIN32OLE_VARIANT_M_H
#define WIN32OLE_VARIANT_M_H 1
-VALUE mWIN32OLE_VARIANT;
+extern VALUE mWIN32OLE_VARIANT;
void Init_win32ole_variant_m(void);
#endif
diff --git a/file.c b/file.c
index 6721d9b406..94ac327e74 100644
--- a/file.c
+++ b/file.c
@@ -3425,21 +3425,42 @@ rb_default_home_dir(VALUE result)
#if defined HAVE_PWD_H
if (!dir) {
- const char *login = getlogin();
- if (login) {
- struct passwd *pw = getpwnam(login);
- if (pw) {
- copy_home_path(result, pw->pw_dir);
- endpwent();
- return result;
- }
- endpwent();
- rb_raise(rb_eArgError, "couldn't find HOME for login `%s' -- expanding `~'",
- login);
- }
- else {
- rb_raise(rb_eArgError, "couldn't find login name -- expanding `~'");
- }
+ /* We'll look up the user's default home dir in the password db by
+ * login name, if possible, and failing that will fall back to looking
+ * the information up by uid (as would be needed for processes that
+ * are not a descendant of login(1) or a work-alike).
+ *
+ * While the lookup by uid is more likely to succeed (since we always
+ * have a uid, but may or may not have a login name), we prefer first
+ * looking up by name to accommodate the possibility of multiple login
+ * names (each with its own record in the password database, so each
+ * with a potentially different home directory) being mapped to the
+ * same uid (as explicitly allowed for by POSIX; see getlogin(3posix)).
+ */
+ VALUE login_name = rb_getlogin();
+
+# if !defined(HAVE_GETPWUID_R) && !defined(HAVE_GETPWUID)
+ /* This is a corner case, but for backward compatibility reasons we
+ * want to emit this error if neither the lookup by login name nor
+ * lookup by getuid() has a chance of succeeding.
+ */
+ if (NIL_P(login_name)) {
+ rb_raise(rb_eArgError, "couldn't find login name -- expanding `~'");
+ }
+# endif
+
+ VALUE pw_dir = rb_getpwdirnam_for_login(login_name);
+ if (NIL_P(pw_dir)) {
+ pw_dir = rb_getpwdiruid();
+ if (NIL_P(pw_dir)) {
+ rb_raise(rb_eArgError, "couldn't find home for uid `%ld'", (long)getuid());
+ }
+ }
+
+ /* found it */
+ copy_home_path(result, RSTRING_PTR(pw_dir));
+ rb_str_resize(pw_dir, 0);
+ return result;
}
#endif
if (!dir) {
diff --git a/hash.c b/hash.c
index e8be71bec3..65a0419af3 100644
--- a/hash.c
+++ b/hash.c
@@ -5652,13 +5652,29 @@ env_invert(void)
return rb_hash_invert(env_to_hash());
}
+static void
+keylist_delete(VALUE keys, VALUE key)
+{
+ long keylen, elen, i;
+ const char *keyptr, *eptr;
+ RSTRING_GETMEM(key, keyptr, keylen);
+ for (i=0; i<RARRAY_LEN(keys); i++) {
+ VALUE e = RARRAY_AREF(keys, i);
+ RSTRING_GETMEM(e, eptr, elen);
+ if (elen != keylen) continue;
+ if (!ENVNMATCH(keyptr, eptr, elen)) continue;
+ rb_ary_delete_at(keys, i);
+ return;
+ }
+}
+
static int
env_replace_i(VALUE key, VALUE val, VALUE keys)
{
+ env_name(key);
env_aset(Qnil, key, val);
- if (rb_ary_includes(keys, key)) {
- rb_ary_delete(keys, key);
- }
+
+ keylist_delete(keys, key);
return ST_CONTINUE;
}
diff --git a/internal.h b/internal.h
index 9583e8388e..13a0424344 100644
--- a/internal.h
+++ b/internal.h
@@ -1682,9 +1682,11 @@ VALUE rb_float_uminus(VALUE num);
VALUE rb_int_plus(VALUE x, VALUE y);
VALUE rb_float_plus(VALUE x, VALUE y);
VALUE rb_int_minus(VALUE x, VALUE y);
+VALUE rb_float_minus(VALUE x, VALUE y);
VALUE rb_int_mul(VALUE x, VALUE y);
VALUE rb_float_mul(VALUE x, VALUE y);
VALUE rb_int_idiv(VALUE x, VALUE y);
+VALUE rb_float_div(VALUE x, VALUE y);
VALUE rb_int_modulo(VALUE x, VALUE y);
VALUE rb_int_round(VALUE num, int ndigits, enum ruby_num_rounding_mode mode);
VALUE rb_int2str(VALUE num, int base);
@@ -1709,6 +1711,8 @@ VALUE rb_int_odd_p(VALUE num);
int rb_int_positive_p(VALUE num);
int rb_int_negative_p(VALUE num);
VALUE rb_num_pow(VALUE x, VALUE y);
+VALUE rb_float_floor(VALUE x, int ndigits);
+
static inline VALUE
rb_num_compare_with_zero(VALUE num, ID mid)
@@ -1975,13 +1979,16 @@ void rb_last_status_clear(void);
VALUE rb_rational_canonicalize(VALUE x);
VALUE rb_rational_uminus(VALUE self);
VALUE rb_rational_plus(VALUE self, VALUE other);
+VALUE rb_rational_minus(VALUE self, VALUE other);
VALUE rb_rational_mul(VALUE self, VALUE other);
+VALUE rb_rational_div(VALUE self, VALUE other);
VALUE rb_lcm(VALUE x, VALUE y);
VALUE rb_rational_reciprocal(VALUE x);
VALUE rb_cstr_to_rat(const char *, int);
VALUE rb_rational_abs(VALUE self);
VALUE rb_rational_cmp(VALUE self, VALUE other);
VALUE rb_rational_pow(VALUE self, VALUE other);
+VALUE rb_rational_floor(VALUE self, int ndigits);
VALUE rb_numeric_quo(VALUE x, VALUE y);
/* re.c */
@@ -2303,6 +2310,12 @@ void rb_write_error_str(VALUE mesg);
/* numeric.c (export) */
VALUE rb_int_positive_pow(long x, unsigned long y);
+#ifdef HAVE_PWD_H
+VALUE rb_getlogin(void);
+VALUE rb_getpwdirnam_for_login(VALUE login); /* read as: "get pwd db home dir by username for login" */
+VALUE rb_getpwdiruid(void); /* read as: "get pwd db home dir for getuid()" */
+#endif
+
/* process.c (export) */
int rb_exec_async_signal_safe(const struct rb_execarg *e, char *errmsg, size_t errmsg_buflen);
rb_pid_t rb_fork_async_signal_safe(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALUE fds, char *errmsg, size_t errmsg_buflen);
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index 9a0d41e2b8..9e0908386e 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -165,7 +165,6 @@ class CGI
raw_cookie.split(/;\s?/).each do |pairs|
name, values = pairs.split('=',2)
next unless name and values
- name = CGI.unescape(name)
values ||= ""
values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) }
if cookies.has_key?(name)
diff --git a/lib/fileutils.rb b/lib/fileutils.rb
index 8981ef98e8..6332fcd6f1 100644
--- a/lib/fileutils.rb
+++ b/lib/fileutils.rb
@@ -1320,6 +1320,7 @@ module FileUtils
else
File.chmod mode, path()
end
+ rescue Errno::EOPNOTSUPP
end
def chown(uid, gid)
@@ -1411,7 +1412,7 @@ module FileUtils
if st.symlink?
begin
File.lchmod mode, path
- rescue NotImplementedError
+ rescue NotImplementedError, Errno::EOPNOTSUPP
end
else
File.chmod mode, path
diff --git a/lib/net/ftp.rb b/lib/net/ftp.rb
index e68d825dcf..c5d669d898 100644
--- a/lib/net/ftp.rb
+++ b/lib/net/ftp.rb
@@ -97,6 +97,10 @@ module Net
# When +true+, the connection is in passive mode. Default: +true+.
attr_accessor :passive
+ # When +true+, use the IP address in PASV responses. Otherwise, it uses
+ # the same IP address for the control connection. Default: +false+.
+ attr_accessor :use_pasv_ip
+
# When +true+, all traffic to and from the server is written
# to +$stdout+. Default: +false+.
attr_accessor :debug_mode
@@ -205,6 +209,9 @@ module Net
# handshake.
# See Net::FTP#ssl_handshake_timeout for
# details. Default: +nil+.
+ # use_pasv_ip:: When +true+, use the IP address in PASV responses.
+ # Otherwise, it uses the same IP address for the control
+ # connection. Default: +false+.
# debug_mode:: When +true+, all traffic to and from the server is
# written to +$stdout+. Default: +false+.
#
@@ -265,6 +272,7 @@ module Net
@open_timeout = options[:open_timeout]
@ssl_handshake_timeout = options[:ssl_handshake_timeout]
@read_timeout = options[:read_timeout] || 60
+ @use_pasv_ip = options[:use_pasv_ip] || false
if host
connect(host, options[:port] || FTP_PORT)
if options[:username]
@@ -1330,7 +1338,12 @@ module Net
raise FTPReplyError, resp
end
if m = /\((?<host>\d+(,\d+){3}),(?<port>\d+,\d+)\)/.match(resp)
- return parse_pasv_ipv4_host(m["host"]), parse_pasv_port(m["port"])
+ if @use_pasv_ip
+ host = parse_pasv_ipv4_host(m["host"])
+ else
+ host = @bare_sock.remote_address.ip_address
+ end
+ return host, parse_pasv_port(m["port"])
else
raise FTPProtoError, resp
end
diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb
index 66132985d9..ca0a6eb379 100644
--- a/lib/net/http/response.rb
+++ b/lib/net/http/response.rb
@@ -262,12 +262,13 @@ class Net::HTTPResponse
begin
yield inflate_body_io
+ success = true
ensure
- orig_err = $!
begin
inflate_body_io.finish
rescue => err
- raise orig_err || err
+ # Ignore #finish's error if there is an exception from yield
+ raise err if success
end
end
when 'none', 'identity' then
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index 1c7e89ba14..91df89b79e 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -1215,12 +1215,14 @@ module Net
end
resp = @tagged_responses.delete(tag)
case resp.name
+ when /\A(?:OK)\z/ni
+ return resp
when /\A(?:NO)\z/ni
raise NoResponseError, resp
when /\A(?:BAD)\z/ni
raise BadResponseError, resp
else
- return resp
+ raise UnknownResponseError, resp
end
end
@@ -3716,6 +3718,10 @@ module Net
class ByeResponseError < ResponseError
end
+ # Error raised upon an unknown response from the server.
+ class UnknownResponseError < ResponseError
+ end
+
RESPONSE_ERRORS = Hash.new(ResponseError)
RESPONSE_ERRORS["NO"] = NoResponseError
RESPONSE_ERRORS["BAD"] = BadResponseError
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index ca2c1abefd..46aace7839 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -430,7 +430,7 @@ The internal error was:
files.reject do |file|
file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)$/i or
(file =~ /tags$/i and
- open(file, 'rb') { |io|
+ File.open(file, 'rb') { |io|
io.read(100) =~ /\A(\f\n[^,]+,\d+$|!_TAG_)/
})
end
diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb
index 3291414597..dc1ea6cbb1 100644
--- a/lib/rdoc/version.rb
+++ b/lib/rdoc/version.rb
@@ -3,6 +3,6 @@ module RDoc
##
# RDoc version you are using
- VERSION = '6.1.2'
+ VERSION = '6.1.2.1'
end
diff --git a/lib/resolv.rb b/lib/resolv.rb
index edca92689d..02c2dedc49 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -691,13 +691,13 @@ class Resolv
rescue DecodeError
next # broken DNS message ignored
end
- if s = sender_for(from, msg)
+ if sender == sender_for(from, msg)
break
else
# unexpected DNS message ignored
end
end
- return msg, s.data
+ return msg, sender.data
end
def sender_for(addr, msg)
diff --git a/lib/rexml/doctype.rb b/lib/rexml/doctype.rb
index 757b639639..a4e91529ac 100644
--- a/lib/rexml/doctype.rb
+++ b/lib/rexml/doctype.rb
@@ -7,6 +7,44 @@ require_relative 'attlistdecl'
require_relative 'xmltokens'
module REXML
+ class ReferenceWriter
+ def initialize(id_type,
+ public_id_literal,
+ system_literal,
+ context=nil)
+ @id_type = id_type
+ @public_id_literal = public_id_literal
+ @system_literal = system_literal
+ if context and context[:prologue_quote] == :apostrophe
+ @default_quote = "'"
+ else
+ @default_quote = "\""
+ end
+ end
+
+ def write(output)
+ output << " #{@id_type}"
+ if @public_id_literal
+ if @public_id_literal.include?("'")
+ quote = "\""
+ else
+ quote = @default_quote
+ end
+ output << " #{quote}#{@public_id_literal}#{quote}"
+ end
+ if @system_literal
+ if @system_literal.include?("'")
+ quote = "\""
+ elsif @system_literal.include?("\"")
+ quote = "'"
+ else
+ quote = @default_quote
+ end
+ output << " #{quote}#{@system_literal}#{quote}"
+ end
+ end
+ end
+
# Represents an XML DOCTYPE declaration; that is, the contents of <!DOCTYPE
# ... >. DOCTYPES can be used to declare the DTD of a document, as well as
# being used to declare entities used in the document.
@@ -50,6 +88,8 @@ module REXML
super( parent )
@name = first.name
@external_id = first.external_id
+ @long_name = first.instance_variable_get(:@long_name)
+ @uri = first.instance_variable_get(:@uri)
elsif first.kind_of? Array
super( parent )
@name = first[0]
@@ -108,19 +148,17 @@ module REXML
# Ignored
def write( output, indent=0, transitive=false, ie_hack=false )
f = REXML::Formatters::Default.new
- c = context
- if c and c[:prologue_quote] == :apostrophe
- quote = "'"
- else
- quote = "\""
- end
indent( output, indent )
output << START
output << ' '
output << @name
- output << " #{@external_id}" if @external_id
- output << " #{quote}#{@long_name}#{quote}" if @long_name
- output << " #{quote}#{@uri}#{quote}" if @uri
+ if @external_id
+ reference_writer = ReferenceWriter.new(@external_id,
+ @long_name,
+ @uri,
+ context)
+ reference_writer.write(output)
+ end
unless @children.empty?
output << ' ['
@children.each { |child|
@@ -259,16 +297,11 @@ module REXML
end
def to_s
- c = nil
- c = parent.context if parent
- if c and c[:prologue_quote] == :apostrophe
- quote = "'"
- else
- quote = "\""
- end
- notation = "<!NOTATION #{@name} #{@middle}"
- notation << " #{quote}#{@public}#{quote}" if @public
- notation << " #{quote}#{@system}#{quote}" if @system
+ context = nil
+ context = parent.context if parent
+ notation = "<!NOTATION #{@name}"
+ reference_writer = ReferenceWriter.new(@middle, @public, @system, context)
+ reference_writer.write(notation)
notation << ">"
notation
end
diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
index 39e9ec3fb1..b10cdcd30e 100644
--- a/lib/rexml/parsers/baseparser.rb
+++ b/lib/rexml/parsers/baseparser.rb
@@ -50,7 +50,6 @@ module REXML
DOCTYPE_START = /\A\s*<!DOCTYPE\s/um
DOCTYPE_END = /\A\s*\]\s*>/um
- DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
ATTRIBUTE_PATTERN = /\s*(#{QNAME_STR})\s*=\s*(["'])(.*?)\4/um
COMMENT_START = /\A<!--/u
COMMENT_PATTERN = /<!--(.*?)-->/um
@@ -61,15 +60,14 @@ module REXML
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>/um
INSTRUCTION_START = /\A<\?/u
INSTRUCTION_PATTERN = /<\?#{NAME}(\s+.*?)?\?>/um
- TAG_MATCH = /^<((?>#{QNAME_STR}))/um
- CLOSE_MATCH = /^\s*<\/(#{QNAME_STR})\s*>/um
+ TAG_MATCH = /\A<((?>#{QNAME_STR}))/um
+ CLOSE_MATCH = /\A\s*<\/(#{QNAME_STR})\s*>/um
VERSION = /\bversion\s*=\s*["'](.*?)['"]/um
ENCODING = /\bencoding\s*=\s*["'](.*?)['"]/um
STANDALONE = /\bstandalone\s*=\s*["'](.*?)['"]/um
ENTITY_START = /\A\s*<!ENTITY/
- IDENTITY = /^([!\*\w\-]+)(\s+#{NCNAME_STR})?(\s+["'](.*?)['"])?(\s+['"](.*?)["'])?/u
ELEMENTDECL_START = /\A\s*<!ELEMENT/um
ELEMENTDECL_PATTERN = /\A\s*(<!ELEMENT.*?)>/um
SYSTEMENTITY = /\A\s*(%.*?;)\s*$/um
@@ -83,9 +81,6 @@ module REXML
ATTDEF_RE = /#{ATTDEF}/
ATTLISTDECL_START = /\A\s*<!ATTLIST/um
ATTLISTDECL_PATTERN = /\A\s*<!ATTLIST\s+#{NAME}(?:#{ATTDEF})*\s*>/um
- NOTATIONDECL_START = /\A\s*<!NOTATION/um
- PUBLIC = /\A\s*<!NOTATION\s+(\w[\-\w]*)\s+(PUBLIC)\s+(["'])(.*?)\3(?:\s+(["'])(.*?)\5)?\s*>/um
- SYSTEM = /\A\s*<!NOTATION\s+(\w[\-\w]*)\s+(SYSTEM)\s+(["'])(.*?)\3\s*>/um
TEXT_PATTERN = /\A([^<]*)/um
@@ -103,6 +98,11 @@ module REXML
GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
+ NOTATIONDECL_START = /\A\s*<!NOTATION/um
+ EXTERNAL_ID_PUBLIC = /\A\s*PUBLIC\s+#{PUBIDLITERAL}\s+#{SYSTEMLITERAL}\s*/um
+ EXTERNAL_ID_SYSTEM = /\A\s*SYSTEM\s+#{SYSTEMLITERAL}\s*/um
+ PUBLIC_ID = /\A\s*PUBLIC\s+#{PUBIDLITERAL}\s*/um
+
EREFERENCE = /&(?!#{NAME};)/
DEFAULT_ENTITIES = {
@@ -195,11 +195,9 @@ module REXML
return [ :end_document ] if empty?
return @stack.shift if @stack.size > 0
#STDERR.puts @source.encoding
- @source.read if @source.buffer.size<2
#STDERR.puts "BUFFER = #{@source.buffer.inspect}"
if @document_status == nil
- #@source.consume( /^\s*/um )
- word = @source.match( /^((?:\s+)|(?:<[^>]*>))/um )
+ word = @source.match( /\A((?:\s+)|(?:<[^>]*>))/um )
word = word[1] unless word.nil?
#STDERR.puts "WORD = #{word.inspect}"
case word
@@ -224,38 +222,49 @@ module REXML
when INSTRUCTION_START
return process_instruction
when DOCTYPE_START
- md = @source.match( DOCTYPE_PATTERN, true )
+ base_error_message = "Malformed DOCTYPE"
+ @source.match(DOCTYPE_START, true)
@nsstack.unshift(curr_ns=Set.new)
- identity = md[1]
- close = md[2]
- identity =~ IDENTITY
- name = $1
- raise REXML::ParseException.new("DOCTYPE is missing a name") if name.nil?
- pub_sys = $2.nil? ? nil : $2.strip
- long_name = $4.nil? ? nil : $4.strip
- uri = $6.nil? ? nil : $6.strip
- args = [ :start_doctype, name, pub_sys, long_name, uri ]
- if close == ">"
+ name = parse_name(base_error_message)
+ if @source.match(/\A\s*\[/um, true)
+ id = [nil, nil, nil]
+ @document_status = :in_doctype
+ elsif @source.match(/\A\s*>/um, true)
+ id = [nil, nil, nil]
@document_status = :after_doctype
- @source.read if @source.buffer.size<2
- md = @source.match(/^\s*/um, true)
- @stack << [ :end_doctype ]
else
- @document_status = :in_doctype
+ id = parse_id(base_error_message,
+ accept_external_id: true,
+ accept_public_id: false)
+ if id[0] == "SYSTEM"
+ # For backward compatibility
+ id[1], id[2] = id[2], nil
+ end
+ if @source.match(/\A\s*\[/um, true)
+ @document_status = :in_doctype
+ elsif @source.match(/\A\s*>/um, true)
+ @document_status = :after_doctype
+ else
+ message = "#{base_error_message}: garbage after external ID"
+ raise REXML::ParseException.new(message, @source)
+ end
+ end
+ args = [:start_doctype, name, *id]
+ if @document_status == :after_doctype
+ @source.match(/\A\s*/um, true)
+ @stack << [ :end_doctype ]
end
return args
- when /^\s+/
+ when /\A\s+/
else
@document_status = :after_doctype
- @source.read if @source.buffer.size<2
- md = @source.match(/\s*/um, true)
if @source.encoding == "UTF-8"
@source.buffer.force_encoding(::Encoding::UTF_8)
end
end
end
if @document_status == :in_doctype
- md = @source.match(/\s*(.*?>)/um)
+ md = @source.match(/\A\s*(.*?>)/um)
case md[1]
when SYSTEMENTITY
match = @source.match( SYSTEMENTITY, true )[1]
@@ -312,24 +321,35 @@ module REXML
end
return [ :attlistdecl, element, pairs, contents ]
when NOTATIONDECL_START
- md = nil
- if @source.match( PUBLIC )
- md = @source.match( PUBLIC, true )
- vals = [md[1],md[2],md[4],md[6]]
- elsif @source.match( SYSTEM )
- md = @source.match( SYSTEM, true )
- vals = [md[1],md[2],nil,md[4]]
- else
- raise REXML::ParseException.new( "error parsing notation: no matching pattern", @source )
+ base_error_message = "Malformed notation declaration"
+ unless @source.match(/\A\s*<!NOTATION\s+/um, true)
+ if @source.match(/\A\s*<!NOTATION\s*>/um)
+ message = "#{base_error_message}: name is missing"
+ else
+ message = "#{base_error_message}: invalid declaration name"
+ end
+ raise REXML::ParseException.new(message, @source)
end
- return [ :notationdecl, *vals ]
+ name = parse_name(base_error_message)
+ id = parse_id(base_error_message,
+ accept_external_id: true,
+ accept_public_id: true)
+ unless @source.match(/\A\s*>/um, true)
+ message = "#{base_error_message}: garbage before end >"
+ raise REXML::ParseException.new(message, @source)
+ end
+ return [:notationdecl, name, *id]
when DOCTYPE_END
@document_status = :after_doctype
@source.match( DOCTYPE_END, true )
return [ :end_doctype ]
end
end
+ if @document_status == :after_doctype
+ @source.match(/\A\s*/um, true)
+ end
begin
+ @source.read if @source.buffer.size<2
if @source.buffer[0] == ?<
if @source.buffer[1] == ?/
@nsstack.shift
@@ -368,6 +388,7 @@ module REXML
unless md
raise REXML::ParseException.new("malformed XML: missing tag start", @source)
end
+ @document_status = :in_element
prefixes = Set.new
prefixes << md[2] if md[2]
@nsstack.unshift(curr_ns=Set.new)
@@ -473,6 +494,85 @@ module REXML
true
end
+ def parse_name(base_error_message)
+ md = @source.match(/\A\s*#{NAME}/um, true)
+ unless md
+ if @source.match(/\A\s*\S/um)
+ message = "#{base_error_message}: invalid name"
+ else
+ message = "#{base_error_message}: name is missing"
+ end
+ raise REXML::ParseException.new(message, @source)
+ end
+ md[1]
+ end
+
+ def parse_id(base_error_message,
+ accept_external_id:,
+ accept_public_id:)
+ if accept_external_id and (md = @source.match(EXTERNAL_ID_PUBLIC, true))
+ pubid = system = nil
+ pubid_literal = md[1]
+ pubid = pubid_literal[1..-2] if pubid_literal # Remove quote
+ system_literal = md[2]
+ system = system_literal[1..-2] if system_literal # Remove quote
+ ["PUBLIC", pubid, system]
+ elsif accept_public_id and (md = @source.match(PUBLIC_ID, true))
+ pubid = system = nil
+ pubid_literal = md[1]
+ pubid = pubid_literal[1..-2] if pubid_literal # Remove quote
+ ["PUBLIC", pubid, nil]
+ elsif accept_external_id and (md = @source.match(EXTERNAL_ID_SYSTEM, true))
+ system = nil
+ system_literal = md[1]
+ system = system_literal[1..-2] if system_literal # Remove quote
+ ["SYSTEM", nil, system]
+ else
+ details = parse_id_invalid_details(accept_external_id: accept_external_id,
+ accept_public_id: accept_public_id)
+ message = "#{base_error_message}: #{details}"
+ raise REXML::ParseException.new(message, @source)
+ end
+ end
+
+ def parse_id_invalid_details(accept_external_id:,
+ accept_public_id:)
+ public = /\A\s*PUBLIC/um
+ system = /\A\s*SYSTEM/um
+ if (accept_external_id or accept_public_id) and @source.match(/#{public}/um)
+ if @source.match(/#{public}(?:\s+[^'"]|\s*[\[>])/um)
+ return "public ID literal is missing"
+ end
+ unless @source.match(/#{public}\s+#{PUBIDLITERAL}/um)
+ return "invalid public ID literal"
+ end
+ if accept_public_id
+ if @source.match(/#{public}\s+#{PUBIDLITERAL}\s+[^'"]/um)
+ return "system ID literal is missing"
+ end
+ unless @source.match(/#{public}\s+#{PUBIDLITERAL}\s+#{SYSTEMLITERAL}/um)
+ return "invalid system literal"
+ end
+ "garbage after system literal"
+ else
+ "garbage after public ID literal"
+ end
+ elsif accept_external_id and @source.match(/#{system}/um)
+ if @source.match(/#{system}(?:\s+[^'"]|\s*[\[>])/um)
+ return "system literal is missing"
+ end
+ unless @source.match(/#{system}\s+#{SYSTEMLITERAL}/um)
+ return "invalid system literal"
+ end
+ "garbage after system literal"
+ else
+ unless @source.match(/\A\s*(?:PUBLIC|SYSTEM)\s/um)
+ return "invalid ID type"
+ end
+ "ID type is missing"
+ end
+ end
+
def process_instruction
match_data = @source.match(INSTRUCTION_PATTERN, true)
unless match_data
diff --git a/lib/rexml/rexml.rb b/lib/rexml/rexml.rb
index 7851c78bf5..627dff4768 100644
--- a/lib/rexml/rexml.rb
+++ b/lib/rexml/rexml.rb
@@ -24,7 +24,7 @@
module REXML
COPYRIGHT = "Copyright © 2001-2008 Sean Russell <ser@germane-software.com>"
DATE = "2008/019"
- VERSION = "3.1.9"
+ VERSION = "3.1.9.1"
REVISION = ""
Copyright = COPYRIGHT
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 616887838c..62fc54a202 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -9,7 +9,7 @@
require 'rbconfig'
module Gem
- VERSION = "3.0.3".freeze
+ VERSION = "3.0.3.1".freeze
end
# Must be first since it unloads the prelude from 1.9.2
diff --git a/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem b/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem
deleted file mode 100644
index 9e6810ab70..0000000000
--- a/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem
+++ /dev/null
@@ -1,23 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
-ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
-LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
-RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
-+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
-PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
-xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
-Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
-hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
-EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
-FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
-nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
-eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
-hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
-Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
-vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
-+OkuE6N36B9K
------END CERTIFICATE-----
diff --git a/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem b/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem
deleted file mode 100644
index 20585f1c01..0000000000
--- a/lib/rubygems/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem
+++ /dev/null
@@ -1,25 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
-MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
-IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
-MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
-FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
-bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
-dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
-H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
-uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
-mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
-a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
-E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
-WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
-VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
-Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
-cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
-IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
-AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
-YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
-6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
-Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
-c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
-mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
------END CERTIFICATE-----
diff --git a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem
index f4ce4ca43d..f4ce4ca43d 100644
--- a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem
+++ b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem
diff --git a/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem
new file mode 100644
index 0000000000..8afb219058
--- /dev/null
+++ b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
+MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
+RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
+gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
+KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
+QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
+XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
+LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
+RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
+jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
+6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
+mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
+Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
+WD9f
+-----END CERTIFICATE-----
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
index 87e53a83be..9d61595ec2 100644
--- a/lib/tmpdir.rb
+++ b/lib/tmpdir.rb
@@ -90,7 +90,7 @@ class Dir
}
if block_given?
begin
- yield path
+ yield path.dup
ensure
unless base
stat = File.stat(File.dirname(path))
diff --git a/lib/uri/ldap.rb b/lib/uri/ldap.rb
index 228c793cbd..61ec3d3f12 100644
--- a/lib/uri/ldap.rb
+++ b/lib/uri/ldap.rb
@@ -119,6 +119,7 @@ module URI
# Private method to cleanup +dn+ from using the +path+ component attribute.
def parse_dn
+ raise InvalidURIError, 'bad LDAP URL' unless @path
@dn = @path[1..-1]
end
private :parse_dn
diff --git a/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb
index e402099a30..c73f48c6df 100644
--- a/lib/webrick/httprequest.rb
+++ b/lib/webrick/httprequest.rb
@@ -226,9 +226,9 @@ module WEBrick
raise HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'."
end
- if /close/io =~ self["connection"]
+ if /\Aclose\z/io =~ self["connection"]
@keep_alive = false
- elsif /keep-alive/io =~ self["connection"]
+ elsif /\Akeep-alive\z/io =~ self["connection"]
@keep_alive = true
elsif @http_version < "1.1"
@keep_alive = false
@@ -503,7 +503,7 @@ module WEBrick
return unless socket
if tc = self['transfer-encoding']
case tc
- when /chunked/io then read_chunked(socket, block)
+ when /\Achunked\z/io then read_chunked(socket, block)
else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
end
elsif self['content-length'] || @remaining_size
diff --git a/lib/webrick/version.rb b/lib/webrick/version.rb
index ee6b415eef..c23df9a912 100644
--- a/lib/webrick/version.rb
+++ b/lib/webrick/version.rb
@@ -14,5 +14,5 @@ module WEBrick
##
# The WEBrick version
- VERSION = "1.4.2"
+ VERSION = "1.4.4"
end
diff --git a/numeric.c b/numeric.c
index d602d0afde..f5ae09b3fd 100644
--- a/numeric.c
+++ b/numeric.c
@@ -1042,8 +1042,8 @@ rb_float_plus(VALUE x, VALUE y)
* Returns a new Float which is the difference of +float+ and +other+.
*/
-static VALUE
-flo_minus(VALUE x, VALUE y)
+VALUE
+rb_float_minus(VALUE x, VALUE y)
{
if (RB_TYPE_P(y, T_FIXNUM)) {
return DBL2NUM(RFLOAT_VALUE(x) - (double)FIX2LONG(y));
@@ -1120,8 +1120,8 @@ rb_flo_div_flo(VALUE x, VALUE y)
* Returns a new Float which is the result of dividing +float+ by +other+.
*/
-static VALUE
-flo_div(VALUE x, VALUE y)
+VALUE
+rb_float_div(VALUE x, VALUE y)
{
double num = RFLOAT_VALUE(x);
double den;
@@ -1891,6 +1891,31 @@ flo_prev_float(VALUE vx)
return DBL2NUM(y);
}
+VALUE
+rb_float_floor(VALUE num, int ndigits)
+{
+ double number, f;
+ number = RFLOAT_VALUE(num);
+ if (number == 0.0) {
+ return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
+ }
+ if (ndigits > 0) {
+ int binexp;
+ frexp(number, &binexp);
+ if (float_round_overflow(ndigits, binexp)) return num;
+ if (number > 0.0 && float_round_underflow(ndigits, binexp))
+ return DBL2NUM(0.0);
+ f = pow(10, ndigits);
+ f = floor(number * f) / f;
+ return DBL2NUM(f);
+ }
+ else {
+ num = dbl2ival(floor(number));
+ if (ndigits < 0) num = rb_int_floor(num, ndigits);
+ return num;
+ }
+}
+
/*
* call-seq:
* float.floor([ndigits]) -> integer or float
@@ -1933,31 +1958,11 @@ flo_prev_float(VALUE vx)
static VALUE
flo_floor(int argc, VALUE *argv, VALUE num)
{
- double number, f;
int ndigits = 0;
-
if (rb_check_arity(argc, 0, 1)) {
ndigits = NUM2INT(argv[0]);
}
- number = RFLOAT_VALUE(num);
- if (number == 0.0) {
- return ndigits > 0 ? DBL2NUM(number) : INT2FIX(0);
- }
- if (ndigits > 0) {
- int binexp;
- frexp(number, &binexp);
- if (float_round_overflow(ndigits, binexp)) return num;
- if (number > 0.0 && float_round_underflow(ndigits, binexp))
- return DBL2NUM(0.0);
- f = pow(10, ndigits);
- f = floor(number * f) / f;
- return DBL2NUM(f);
- }
- else {
- num = dbl2ival(floor(number));
- if (ndigits < 0) num = rb_int_floor(num, ndigits);
- return num;
- }
+ return rb_float_floor(num, ndigits);
}
/*
@@ -5676,9 +5681,9 @@ Init_Numeric(void)
rb_define_method(rb_cFloat, "coerce", flo_coerce, 1);
rb_define_method(rb_cFloat, "-@", rb_float_uminus, 0);
rb_define_method(rb_cFloat, "+", rb_float_plus, 1);
- rb_define_method(rb_cFloat, "-", flo_minus, 1);
+ rb_define_method(rb_cFloat, "-", rb_float_minus, 1);
rb_define_method(rb_cFloat, "*", rb_float_mul, 1);
- rb_define_method(rb_cFloat, "/", flo_div, 1);
+ rb_define_method(rb_cFloat, "/", rb_float_div, 1);
rb_define_method(rb_cFloat, "quo", flo_quo, 1);
rb_define_method(rb_cFloat, "fdiv", flo_quo, 1);
rb_define_method(rb_cFloat, "%", flo_mod, 1);
diff --git a/object.c b/object.c
index 454d35790f..f746c90d55 100644
--- a/object.c
+++ b/object.c
@@ -2089,6 +2089,9 @@ rb_undefined_alloc(VALUE klass)
klass);
}
+static rb_alloc_func_t class_get_alloc_func(VALUE klass);
+static VALUE class_call_alloc_func(rb_alloc_func_t allocator, VALUE klass);
+
/*
* call-seq:
* class.allocate() -> obj
@@ -2112,9 +2115,26 @@ rb_undefined_alloc(VALUE klass)
*/
static VALUE
+rb_class_alloc_m(VALUE klass)
+{
+ rb_alloc_func_t allocator = class_get_alloc_func(klass);
+ if (!rb_obj_respond_to(klass, rb_intern("allocate"), 1)) {
+ rb_raise(rb_eTypeError, "calling %"PRIsVALUE".allocate is prohibited",
+ klass);
+ }
+ return class_call_alloc_func(allocator, klass);
+}
+
+static VALUE
rb_class_alloc(VALUE klass)
{
- VALUE obj;
+ rb_alloc_func_t allocator = class_get_alloc_func(klass);
+ return class_call_alloc_func(allocator, klass);
+}
+
+static rb_alloc_func_t
+class_get_alloc_func(VALUE klass)
+{
rb_alloc_func_t allocator;
if (RCLASS_SUPER(klass) == 0 && klass != rb_cBasicObject) {
@@ -2127,6 +2147,13 @@ rb_class_alloc(VALUE klass)
if (!allocator) {
rb_undefined_alloc(klass);
}
+ return allocator;
+}
+
+static VALUE
+class_call_alloc_func(rb_alloc_func_t allocator, VALUE klass)
+{
+ VALUE obj;
RUBY_DTRACE_CREATE_HOOK(OBJECT, rb_class2name(klass));
@@ -4268,7 +4295,7 @@ InitVM_Object(void)
rb_define_method(rb_cModule, "deprecate_constant", rb_mod_deprecate_constant, -1); /* in variable.c */
rb_define_method(rb_cModule, "singleton_class?", rb_mod_singleton_p, 0);
- rb_define_method(rb_cClass, "allocate", rb_class_alloc, 0);
+ rb_define_method(rb_cClass, "allocate", rb_class_alloc_m, 0);
rb_define_method(rb_cClass, "new", rb_class_s_new, -1);
rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
diff --git a/parse.y b/parse.y
index 16bde4db02..2b5a692558 100644
--- a/parse.y
+++ b/parse.y
@@ -758,7 +758,8 @@ static void token_info_pop(struct parser_params*, const char *token, const rb_co
static void token_info_warn(struct parser_params *p, const char *token, token_info *ptinfo_beg, int same, const rb_code_location_t *loc);
%}
-%pure-parser
+%expect 0
+%define api.pure
%lex-param {struct parser_params *p}
%parse-param {struct parser_params *p}
%initial-action
@@ -8313,16 +8314,17 @@ yylex(YYSTYPE *lval, YYLTYPE *yylloc, struct parser_params *p)
p->lval = lval;
lval->val = Qundef;
t = parser_yylex(p);
- if (has_delayed_token(p))
- dispatch_delayed_token(p, t);
- else if (t != 0)
- dispatch_scan_event(p, t);
if (p->lex.strterm && (p->lex.strterm->flags & STRTERM_HEREDOC))
RUBY_SET_YYLLOC_FROM_STRTERM_HEREDOC(*yylloc);
else
RUBY_SET_YYLLOC(*yylloc);
+ if (has_delayed_token(p))
+ dispatch_delayed_token(p, t);
+ else if (t != 0)
+ dispatch_scan_event(p, t);
+
return t;
}
@@ -9137,7 +9139,7 @@ parser_token_value_print(struct parser_params *p, enum yytokentype type, const Y
#ifndef RIPPER
v = rb_id2str(valp->id);
#else
- v = valp->val;
+ v = valp->node->nd_rval;
#endif
rb_parser_printf(p, "%"PRIsVALUE, v);
break;
@@ -9478,10 +9480,10 @@ node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, const YYLTYPE *loc)
return lhs;
}
-static int
-value_expr_gen(struct parser_params *p, NODE *node)
+static NODE *
+value_expr_check(struct parser_params *p, NODE *node)
{
- int cond = 0;
+ NODE *void_node = 0, *vn;
if (!node) {
rb_warning0("empty expression");
@@ -9493,9 +9495,7 @@ value_expr_gen(struct parser_params *p, NODE *node)
case NODE_NEXT:
case NODE_REDO:
case NODE_RETRY:
- if (!cond) yyerror1(&node->nd_loc, "void value expression");
- /* or "control never reach"? */
- return FALSE;
+ return void_node ? void_node : node;
case NODE_BLOCK:
while (node->nd_next) {
@@ -9518,14 +9518,15 @@ value_expr_gen(struct parser_params *p, NODE *node)
node = node->nd_body;
break;
}
- if (!value_expr(node->nd_body)) return FALSE;
+ vn = value_expr_check(p, node->nd_body);
+ if (!vn) return NULL;
+ if (!void_node) void_node = vn;
node = node->nd_else;
break;
case NODE_AND:
case NODE_OR:
- cond = 1;
- node = node->nd_2nd;
+ node = node->nd_1st;
break;
case NODE_LASGN:
@@ -9533,13 +9534,25 @@ value_expr_gen(struct parser_params *p, NODE *node)
case NODE_DASGN_CURR:
case NODE_MASGN:
mark_lvar_used(p, node);
- return TRUE;
+ return NULL;
default:
- return TRUE;
+ return NULL;
}
}
+ return NULL;
+}
+
+static int
+value_expr_gen(struct parser_params *p, NODE *node)
+{
+ NODE *void_node = value_expr_check(p, node);
+ if (void_node) {
+ yyerror1(&void_node->nd_loc, "void value expression");
+ /* or "control never reach"? */
+ return FALSE;
+ }
return TRUE;
}
@@ -10671,12 +10684,13 @@ reg_named_capture_assign_iter(const OnigUChar *name, const OnigUChar *name_end,
NODE *node, *succ;
if (!len) return ST_CONTINUE;
- if (len < MAX_WORD_LENGTH && rb_reserved_word(s, (int)len))
- return ST_CONTINUE;
if (rb_enc_symname_type(s, len, enc, (1U<<ID_LOCAL)) != ID_LOCAL)
return ST_CONTINUE;
var = intern_cstr(s, len, enc);
+ if (len < MAX_WORD_LENGTH && rb_reserved_word(s, (int)len)) {
+ if (!lvar_defined(p, var)) return ST_CONTINUE;
+ }
node = node_assign(p, assignable(p, var, 0, arg->loc), NEW_LIT(ID2SYM(var), arg->loc), arg->loc);
succ = arg->succ_block;
if (!succ) succ = NEW_BEGIN(0, arg->loc);
diff --git a/proc.c b/proc.c
index dc827c467f..fc1fdf7b3a 100644
--- a/proc.c
+++ b/proc.c
@@ -2100,10 +2100,22 @@ method_clone(VALUE self)
*/
+/* Document-method: Method#[]
+ *
+ * call-seq:
+ * meth[args, ...] -> obj
+ *
+ * Invokes the <i>meth</i> with the specified arguments, returning the
+ * method's return value, like #call.
+ *
+ * m = 12.method("+")
+ * m[3] #=> 15
+ * m[20] #=> 32
+ */
+
/*
* call-seq:
* meth.call(args, ...) -> obj
- * meth[args, ...] -> obj
*
* Invokes the <i>meth</i> with the specified arguments, returning the
* method's return value.
@@ -2635,7 +2647,8 @@ method_inspect(VALUE method)
str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method));
OBJ_INFECT_RAW(str, method);
- mklass = data->klass;
+ mklass = data->iclass;
+ if (!mklass) mklass = data->klass;
if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
defined_class = data->me->def->body.alias.original_me->owner;
@@ -2667,6 +2680,12 @@ method_inspect(VALUE method)
}
}
else {
+ mklass = data->klass;
+ if (FL_TEST(mklass, FL_SINGLETON)) {
+ do {
+ mklass = RCLASS_SUPER(mklass);
+ } while (RB_TYPE_P(mklass, T_ICLASS));
+ }
rb_str_buf_append(str, rb_inspect(mklass));
if (defined_class != mklass) {
rb_str_catf(str, "(% "PRIsVALUE")", defined_class);
@@ -2741,6 +2760,8 @@ method_to_proc(VALUE method)
return procval;
}
+extern VALUE rb_find_defined_class_by_owner(VALUE current_class, VALUE target_owner);
+
/*
* call-seq:
* meth.super_method -> method
@@ -2760,8 +2781,15 @@ method_super_method(VALUE method)
TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
iclass = data->iclass;
if (!iclass) return Qnil;
- super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
- mid = data->me->called_id;
+ if (data->me->def->type == VM_METHOD_TYPE_ALIAS && data->me->defined_class) {
+ super_class = RCLASS_SUPER(rb_find_defined_class_by_owner(data->me->defined_class,
+ data->me->def->body.alias.original_me->owner));
+ mid = data->me->def->body.alias.original_me->def->original_id;
+ }
+ else {
+ super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
+ mid = data->me->def->original_id;
+ }
if (!super_class) return Qnil;
me = (rb_method_entry_t *)rb_callable_method_entry_without_refinements(super_class, mid, &iclass);
if (!me) return Qnil;
@@ -3157,8 +3185,8 @@ rb_method_compose_to_left(VALUE self, VALUE g)
* meth >> g -> a_proc
*
* Returns a proc that is the composition of this method and the given <i>g</i>.
- * The returned proc takes a variable number of arguments, calls <i>g</i> with them
- * then calls this method with the result.
+ * The returned proc takes a variable number of arguments, calls this method
+ * with them then calls <i>g</i> with the result.
*
* def f(x)
* x * x
diff --git a/process.c b/process.c
index 47f4771974..863ee31f90 100644
--- a/process.c
+++ b/process.c
@@ -154,12 +154,40 @@ static int exec_async_signal_safe(const struct rb_execarg *, char *, size_t);
#define p_gid_from_name p_gid_from_name
#endif
+#if defined(HAVE_UNISTD_H)
+# if defined(HAVE_GETLOGIN_R)
+# define USE_GETLOGIN_R 1
+# define GETLOGIN_R_SIZE_DEFAULT 0x100
+# define GETLOGIN_R_SIZE_LIMIT 0x1000
+# if defined(_SC_LOGIN_NAME_MAX)
+# define GETLOGIN_R_SIZE_INIT sysconf(_SC_LOGIN_NAME_MAX)
+# else
+# define GETLOGIN_R_SIZE_INIT GETLOGIN_R_SIZE_DEFAULT
+# endif
+# elif defined(HAVE_GETLOGIN)
+# define USE_GETLOGIN 1
+# endif
+#endif
+
#if defined(HAVE_PWD_H)
-# if defined(HAVE_GETPWNAM_R) && defined(_SC_GETPW_R_SIZE_MAX)
+# if defined(HAVE_GETPWUID_R)
+# define USE_GETPWUID_R 1
+# elif defined(HAVE_GETPWUID)
+# define USE_GETPWUID 1
+# endif
+# if defined(HAVE_GETPWNAM_R)
# define USE_GETPWNAM_R 1
-# define GETPW_R_SIZE_INIT sysconf(_SC_GETPW_R_SIZE_MAX)
+# elif defined(HAVE_GETPWNAM)
+# define USE_GETPWNAM 1
+# endif
+# if defined(HAVE_GETPWNAM_R) || defined(HAVE_GETPWUID_R)
# define GETPW_R_SIZE_DEFAULT 0x1000
# define GETPW_R_SIZE_LIMIT 0x10000
+# if defined(_SC_GETPW_R_SIZE_MAX)
+# define GETPW_R_SIZE_INIT sysconf(_SC_GETPW_R_SIZE_MAX)
+# else
+# define GETPW_R_SIZE_INIT GETPW_R_SIZE_DEFAULT
+# endif
# endif
# ifdef USE_GETPWNAM_R
# define PREPARE_GETPWNAM \
@@ -5462,6 +5490,239 @@ check_gid_switch(void)
}
+#if defined(HAVE_PWD_H)
+/**
+ * Best-effort attempt to obtain the name of the login user, if any,
+ * associated with the process. Processes not descended from login(1) (or
+ * similar) may not have a logged-in user; returns Qnil in that case.
+ */
+VALUE
+rb_getlogin(void)
+{
+#if ( !defined(USE_GETLOGIN_R) && !defined(USE_GETLOGIN) )
+ return Qnil;
+#else
+ char MAYBE_UNUSED(*login) = NULL;
+
+# ifdef USE_GETLOGIN_R
+
+ long loginsize = GETLOGIN_R_SIZE_INIT; /* maybe -1 */
+
+ if (loginsize < 0)
+ loginsize = GETLOGIN_R_SIZE_DEFAULT;
+
+ VALUE maybe_result = rb_str_buf_new(loginsize);
+
+ login = RSTRING_PTR(maybe_result);
+ loginsize = rb_str_capacity(maybe_result);
+ rb_str_set_len(maybe_result, loginsize);
+
+ int gle;
+ errno = 0;
+ while ((gle = getlogin_r(login, loginsize)) != 0) {
+
+ if (gle == ENOTTY || gle == ENXIO || gle == ENOENT) {
+ rb_str_resize(maybe_result, 0);
+ return Qnil;
+ }
+
+ if (gle != ERANGE || loginsize >= GETLOGIN_R_SIZE_LIMIT) {
+ rb_str_resize(maybe_result, 0);
+ rb_syserr_fail(gle, "getlogin_r");
+ }
+
+ rb_str_modify_expand(maybe_result, loginsize);
+ login = RSTRING_PTR(maybe_result);
+ loginsize = rb_str_capacity(maybe_result);
+ }
+
+ if (login == NULL) {
+ rb_str_resize(maybe_result, 0);
+ return Qnil;
+ }
+
+ return maybe_result;
+
+# elif USE_GETLOGIN
+
+ errno = 0;
+ login = getlogin();
+ if (errno) {
+ if (errno == ENOTTY || errno == ENXIO || errno == ENOENT) {
+ return Qnil;
+ }
+ rb_syserr_fail(errno, "getlogin");
+ }
+
+ return login ? rb_str_new_cstr(login) : Qnil;
+# endif
+
+#endif
+}
+
+VALUE
+rb_getpwdirnam_for_login(VALUE login_name)
+{
+#if ( !defined(USE_GETPWNAM_R) && !defined(USE_GETPWNAM) )
+ return Qnil;
+#else
+
+ if (NIL_P(login_name)) {
+ /* nothing to do; no name with which to query the password database */
+ return Qnil;
+ }
+
+ char *login = RSTRING_PTR(login_name);
+
+ struct passwd *pwptr;
+
+# ifdef USE_GETPWNAM_R
+
+ struct passwd pwdnm;
+ char *bufnm;
+ long bufsizenm = GETPW_R_SIZE_INIT; /* maybe -1 */
+
+ if (bufsizenm < 0)
+ bufsizenm = GETPW_R_SIZE_DEFAULT;
+
+ VALUE getpwnm_tmp = rb_str_tmp_new(bufsizenm);
+
+ bufnm = RSTRING_PTR(getpwnm_tmp);
+ bufsizenm = rb_str_capacity(getpwnm_tmp);
+ rb_str_set_len(getpwnm_tmp, bufsizenm);
+
+ int enm;
+ errno = 0;
+ while ((enm = getpwnam_r(login, &pwdnm, bufnm, bufsizenm, &pwptr)) != 0) {
+
+ if (enm == ENOENT || enm== ESRCH || enm == EBADF || enm == EPERM) {
+ /* not found; non-errors */
+ rb_str_resize(getpwnm_tmp, 0);
+ return Qnil;
+ }
+
+ if (enm != ERANGE || bufsizenm >= GETPW_R_SIZE_LIMIT) {
+ rb_str_resize(getpwnm_tmp, 0);
+ rb_syserr_fail(enm, "getpwnam_r");
+ }
+
+ rb_str_modify_expand(getpwnm_tmp, bufsizenm);
+ bufnm = RSTRING_PTR(getpwnm_tmp);
+ bufsizenm = rb_str_capacity(getpwnm_tmp);
+ }
+
+ if (pwptr == NULL) {
+ /* no record in the password database for the login name */
+ rb_str_resize(getpwnm_tmp, 0);
+ return Qnil;
+ }
+
+ /* found it */
+ VALUE result = rb_str_new_cstr(pwptr->pw_dir);
+ rb_str_resize(getpwnm_tmp, 0);
+ return result;
+
+# elif USE_GETPWNAM
+
+ errno = 0;
+ pwptr = getpwnam(login);
+ if (pwptr) {
+ /* found it */
+ return rb_str_new_cstr(pwptr->pw_dir);
+ }
+ if (errno
+ /* avoid treating as errors errno values that indicate "not found" */
+ && ( errno != ENOENT && errno != ESRCH && errno != EBADF && errno != EPERM)) {
+ rb_syserr_fail(errno, "getpwnam");
+ }
+
+ return Qnil; /* not found */
+# endif
+
+#endif
+}
+
+/**
+ * Look up the user's dflt home dir in the password db, by uid.
+ */
+VALUE
+rb_getpwdiruid(void)
+{
+# if !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID)
+ /* Should never happen... </famous-last-words> */
+ return Qnil;
+# else
+ uid_t ruid = getuid();
+
+ struct passwd *pwptr;
+
+# ifdef USE_GETPWUID_R
+
+ struct passwd pwdid;
+ char *bufid;
+ long bufsizeid = GETPW_R_SIZE_INIT; /* maybe -1 */
+
+ if (bufsizeid < 0)
+ bufsizeid = GETPW_R_SIZE_DEFAULT;
+
+ VALUE getpwid_tmp = rb_str_tmp_new(bufsizeid);
+
+ bufid = RSTRING_PTR(getpwid_tmp);
+ bufsizeid = rb_str_capacity(getpwid_tmp);
+ rb_str_set_len(getpwid_tmp, bufsizeid);
+
+ int eid;
+ errno = 0;
+ while ((eid = getpwuid_r(ruid, &pwdid, bufid, bufsizeid, &pwptr)) != 0) {
+ if (eid == ENOENT || eid== ESRCH || eid == EBADF || eid == EPERM) {
+ /* not found; non-errors */
+ rb_str_resize(getpwid_tmp, 0);
+ return Qnil;
+ }
+
+ if (eid != ERANGE || bufsizeid >= GETPW_R_SIZE_LIMIT) {
+ rb_str_resize(getpwid_tmp, 0);
+ rb_syserr_fail(eid, "getpwuid_r");
+ }
+
+ rb_str_modify_expand(getpwid_tmp, bufsizeid);
+ bufid = RSTRING_PTR(getpwid_tmp);
+ bufsizeid = rb_str_capacity(getpwid_tmp);
+ }
+
+ if (pwptr == NULL) {
+ /* no record in the password database for the uid */
+ rb_str_resize(getpwid_tmp, 0);
+ return Qnil;
+ }
+
+ /* found it */
+ VALUE result = rb_str_new_cstr(pwptr->pw_dir);
+ rb_str_resize(getpwid_tmp, 0);
+ return result;
+
+# elif defined(USE_GETPWUID)
+
+ errno = 0;
+ pwptr = getpwuid(ruid);
+ if (pwptr) {
+ /* found it */
+ return rb_str_new_cstr(pwptr->pw_dir);
+ }
+ if (errno
+ /* avoid treating as errors errno values that indicate "not found" */
+ && ( errno == ENOENT || errno == ESRCH || errno == EBADF || errno == EPERM)) {
+ rb_syserr_fail(errno, "getpwuid");
+ }
+
+ return Qnil; /* not found */
+# endif
+
+#endif /* !defined(USE_GETPWUID_R) && !defined(USE_GETPWUID) */
+}
+#endif /* HAVE_PWD_H */
+
+
/*********************************************************************
* Document-class: Process::Sys
*
diff --git a/rational.c b/rational.c
index 435a68f421..5058ce1bfa 100644
--- a/rational.c
+++ b/rational.c
@@ -781,8 +781,8 @@ rb_rational_plus(VALUE self, VALUE other)
* Rational(9, 8) - 4 #=> (-23/8)
* Rational(20, 9) - 9.8 #=> -7.577777777777778
*/
-static VALUE
-nurat_sub(VALUE self, VALUE other)
+VALUE
+rb_rational_minus(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
{
@@ -919,8 +919,8 @@ rb_rational_mul(VALUE self, VALUE other)
* Rational(9, 8) / 4 #=> (9/32)
* Rational(20, 9) / 9.8 #=> 0.22675736961451246
*/
-static VALUE
-nurat_div(VALUE self, VALUE other)
+VALUE
+rb_rational_div(VALUE self, VALUE other)
{
if (RB_INTEGER_TYPE_P(other)) {
if (f_zero_p(other))
@@ -972,10 +972,10 @@ nurat_fdiv(VALUE self, VALUE other)
{
VALUE div;
if (f_zero_p(other))
- return nurat_div(self, rb_float_new(0.0));
+ return rb_rational_div(self, rb_float_new(0.0));
if (FIXNUM_P(other) && other == LONG2FIX(1))
return nurat_to_f(self);
- div = nurat_div(self, other);
+ div = rb_rational_div(self, other);
if (RB_TYPE_P(div, T_RATIONAL))
return nurat_to_f(div);
if (RB_FLOAT_TYPE_P(div))
@@ -1410,7 +1410,7 @@ f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE))
s = (*func)(s);
- s = nurat_div(f_rational_new_bang1(CLASS_OF(self), s), b);
+ s = rb_rational_div(f_rational_new_bang1(CLASS_OF(self), s), b);
if (RB_TYPE_P(s, T_RATIONAL) && FIX2INT(rb_int_cmp(n, ONE)) < 0)
s = nurat_truncate(s);
@@ -1418,6 +1418,18 @@ f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE))
return s;
}
+VALUE
+rb_rational_floor(VALUE self, int ndigits)
+{
+ if (ndigits == 0) {
+ return nurat_floor(self);
+ }
+ else {
+ VALUE n = INT2NUM(ndigits);
+ return f_round_common(1, &n, self, nurat_floor);
+ }
+}
+
/*
* call-seq:
* rat.floor([ndigits]) -> integer or rational
@@ -1865,7 +1877,7 @@ VALUE
rb_rational_reciprocal(VALUE x)
{
get_dat1(x);
- return f_rational_new_no_reduce2(CLASS_OF(x), dat->den, dat->num);
+ return nurat_convert(CLASS_OF(x), dat->den, dat->num, FALSE);
}
/*
@@ -2013,7 +2025,7 @@ rb_numeric_quo(VALUE x, VALUE y)
else {
x = rb_convert_type(x, T_RATIONAL, "Rational", "to_r");
}
- return nurat_div(x, y);
+ return rb_rational_div(x, y);
}
VALUE
@@ -2735,10 +2747,10 @@ Init_Rational(void)
rb_define_method(rb_cRational, "-@", rb_rational_uminus, 0);
rb_define_method(rb_cRational, "+", rb_rational_plus, 1);
- rb_define_method(rb_cRational, "-", nurat_sub, 1);
+ rb_define_method(rb_cRational, "-", rb_rational_minus, 1);
rb_define_method(rb_cRational, "*", rb_rational_mul, 1);
- rb_define_method(rb_cRational, "/", nurat_div, 1);
- rb_define_method(rb_cRational, "quo", nurat_div, 1);
+ rb_define_method(rb_cRational, "/", rb_rational_div, 1);
+ rb_define_method(rb_cRational, "quo", rb_rational_div, 1);
rb_define_method(rb_cRational, "fdiv", nurat_fdiv, 1);
rb_define_method(rb_cRational, "**", nurat_expt, 1);
diff --git a/spec/ruby/core/method/shared/to_s.rb b/spec/ruby/core/method/shared/to_s.rb
index 373398a785..7666322936 100644
--- a/spec/ruby/core/method/shared/to_s.rb
+++ b/spec/ruby/core/method/shared/to_s.rb
@@ -31,4 +31,22 @@ describe :method_to_s, shared: true do
it "returns a String containing the Module the method is referenced from" do
@string.should =~ /MethodSpecs::MySub/
end
+
+ ruby_version_is '2.8' do
+ it "returns a String containing the Module containing the method if object has a singleton class but method is not defined in the singleton class" do
+ obj = MethodSpecs::MySub.new
+ obj.singleton_class
+ @m = obj.method(:bar)
+ @string = @m.send(@method).sub(/0x\w+/, '0xXXXXXX')
+ @string.should =~ /\A#<Method: MethodSpecs::MySub\(MethodSpecs::MyMod\)#bar\(\) /
+ end
+ end
+
+ it "returns a String containing the singleton class if method is defined in the singleton class" do
+ obj = MethodSpecs::MySub.new
+ def obj.bar; end
+ @m = obj.method(:bar)
+ @string = @m.send(@method).sub(/0x\w+/, '0xXXXXXX')
+ @string.should =~ /\A#<Method: #<MethodSpecs::MySub:0xXXXXXX>\.bar/
+ end
end
diff --git a/spec/ruby/library/net/http/http/get_spec.rb b/spec/ruby/library/net/http/http/get_spec.rb
index 4b8af5950a..e6cd50fa27 100644
--- a/spec/ruby/library/net/http/http/get_spec.rb
+++ b/spec/ruby/library/net/http/http/get_spec.rb
@@ -24,3 +24,72 @@ describe "Net::HTTP.get when passed URI" do
end
end
end
+
+quarantine! do # These specs fail frequently with CHECK_LEAKS=true
+describe "Net::HTTP.get" do
+ describe "when reading gzipped contents" do
+ def start_threads
+ require 'zlib'
+
+ server = nil
+ server_thread = Thread.new do
+ server = TCPServer.new("127.0.0.1", 0)
+ begin
+ c = server.accept
+ ensure
+ server.close
+ end
+ c.print "HTTP/1.1 200\r\n"
+ c.print "Content-Type: text/plain\r\n"
+ c.print "Content-Encoding: gzip\r\n"
+ s = StringIO.new
+ z = Zlib::GzipWriter.new(s)
+ begin
+ z.write 'Hello World!'
+ ensure
+ z.close
+ end
+ c.print "Content-Length: #{s.length}\r\n\r\n"
+ # Write partial gzip content
+ c.write s.string.byteslice(0..-2)
+ c.flush
+ c
+ end
+ Thread.pass until server && server_thread.stop?
+
+ client_thread = Thread.new do
+ Thread.current.report_on_exception = false
+ Net::HTTP.get("127.0.0.1", '/', server.connect_address.ip_port)
+ end
+ Thread.pass until client_thread.stop?
+
+ [server_thread, client_thread]
+ end
+
+ it "propagates exceptions interrupting the thread and does not replace it with Zlib::BufError" do
+ my_exception = Class.new(RuntimeError)
+ server_thread, client_thread = start_threads
+ socket = server_thread.value
+ begin
+ client_thread.raise my_exception, "my exception"
+ -> { client_thread.value }.should raise_error(my_exception)
+ ensure
+ socket.close
+ end
+ end
+
+ ruby_version_is "2.8" do # https://bugs.ruby-lang.org/issues/13882#note-6
+ it "lets the kill Thread exception goes through and does not replace it with Zlib::BufError" do
+ server_thread, client_thread = start_threads
+ socket = server_thread.value
+ begin
+ client_thread.kill
+ client_thread.value.should == nil
+ ensure
+ socket.close
+ end
+ end
+ end
+ end
+end
+end
diff --git a/strftime.c b/strftime.c
index dd2b21f977..446be7d338 100644
--- a/strftime.c
+++ b/strftime.c
@@ -326,7 +326,9 @@ rb_strftime_with_timespec(VALUE ftime, const char *format, size_t format_len,
s += len; \
if (i > 0) case_conv(s, i, flags); \
if (precision > i) {\
+ s += i; \
NEEDS(precision); \
+ s -= i; \
memmove(s + precision - i, s, i);\
memset(s, padding ? padding : ' ', precision - i); \
s += precision; \
diff --git a/test/-ext-/debug/test_profile_frames.rb b/test/-ext-/debug/test_profile_frames.rb
index 5ea506046e..0335267ee9 100644
--- a/test/-ext-/debug/test_profile_frames.rb
+++ b/test/-ext-/debug/test_profile_frames.rb
@@ -3,6 +3,16 @@ require 'test/unit'
require '-test-/debug'
class SampleClassForTestProfileFrames
+ class << self
+ attr_accessor :sample4
+ end
+
+ self.sample4 = Module.new do
+ def self.corge(block)
+ Sample2.new.baz(block)
+ end
+ end
+
class Sample2
def baz(block)
instance_eval "def zab(block) block.call end"
@@ -10,8 +20,16 @@ class SampleClassForTestProfileFrames
end
end
+ module Sample3
+ class << self
+ def qux(block)
+ SampleClassForTestProfileFrames.sample4.corge(block)
+ end
+ end
+ end
+
def self.bar(block)
- Sample2.new.baz(block)
+ Sample3.qux(block)
end
def foo(block)
@@ -29,6 +47,8 @@ class TestProfileFrames < Test::Unit::TestCase
"test_profile_frames",
"zab",
"baz",
+ "corge",
+ "qux",
"bar",
"foo",
"test_profile_frames",
@@ -37,6 +57,8 @@ class TestProfileFrames < Test::Unit::TestCase
"test_profile_frames",
"zab",
"baz",
+ "corge",
+ "qux",
"bar",
"foo",
"test_profile_frames",
@@ -45,6 +67,8 @@ class TestProfileFrames < Test::Unit::TestCase
"TestProfileFrames#test_profile_frames",
"#{obj.inspect}.zab",
"SampleClassForTestProfileFrames::Sample2#baz",
+ "#{SampleClassForTestProfileFrames.sample4.inspect}.corge",
+ "SampleClassForTestProfileFrames::Sample3.qux",
"SampleClassForTestProfileFrames.bar",
"SampleClassForTestProfileFrames#foo",
"TestProfileFrames#test_profile_frames",
@@ -53,17 +77,21 @@ class TestProfileFrames < Test::Unit::TestCase
TestProfileFrames,
obj,
SampleClassForTestProfileFrames::Sample2,
+ SampleClassForTestProfileFrames.sample4,
+ SampleClassForTestProfileFrames::Sample3,
SampleClassForTestProfileFrames, # singleton method
SampleClassForTestProfileFrames,
TestProfileFrames,
]
singleton_method_p = [
- false, true, false, true, false, false, false,
+ false, true, false, true, true, true, false, false, false,
]
method_names = [
"test_profile_frames",
"zab",
"baz",
+ "corge",
+ "qux",
"bar",
"foo",
"test_profile_frames",
@@ -72,14 +100,14 @@ class TestProfileFrames < Test::Unit::TestCase
"TestProfileFrames#test_profile_frames",
"#{obj.inspect}.zab",
"SampleClassForTestProfileFrames::Sample2#baz",
+ "#{SampleClassForTestProfileFrames.sample4.inspect}.corge",
+ "SampleClassForTestProfileFrames::Sample3.qux",
"SampleClassForTestProfileFrames.bar",
"SampleClassForTestProfileFrames#foo",
"TestProfileFrames#test_profile_frames",
]
- paths = [ file=__FILE__, "(eval)", file, file, file, file ]
- absolute_paths = [ file, nil, file, file, file, file ]
-
- # pp frames
+ paths = [ file=__FILE__, "(eval)", file, file, file, file, file, file ]
+ absolute_paths = [ file, nil, file, file, file, file, file, file ]
assert_equal(labels.size, frames.size)
diff --git a/test/cgi/test_cgi_cookie.rb b/test/cgi/test_cgi_cookie.rb
index 115a57e4a1..985cc0d7a1 100644
--- a/test/cgi/test_cgi_cookie.rb
+++ b/test/cgi/test_cgi_cookie.rb
@@ -101,6 +101,11 @@ class CGICookieTest < Test::Unit::TestCase
end
end
+ def test_cgi_cookie_parse_not_decode_name
+ cookie_str = "%66oo=baz;foo=bar"
+ cookies = CGI::Cookie.parse(cookie_str)
+ assert_equal({"%66oo" => ["baz"], "foo" => ["bar"]}, cookies)
+ end
def test_cgi_cookie_arrayinterface
cookie = CGI::Cookie.new('name1', 'a', 'b', 'c')
diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb
index 30523c341a..1625d48845 100644
--- a/test/coverage/test_coverage.rb
+++ b/test/coverage/test_coverage.rb
@@ -696,4 +696,16 @@ class TestCoverage < Test::Unit::TestCase
}
}
end
+
+ def test_stop_wrong_peephole_optimization
+ result = {
+ :lines => [1, 1, 1, nil]
+ }
+ assert_coverage(<<~"end;", { lines: true }, result)
+ raise if 1 == 2
+ while true
+ break
+ end
+ end;
+ end
end
diff --git a/test/date/test_date_parse.rb b/test/date/test_date_parse.rb
index e17fd3eb25..d1163b29cb 100644
--- a/test/date/test_date_parse.rb
+++ b/test/date/test_date_parse.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'test/unit'
require 'date'
+require 'timeout'
class TestDateParse < Test::Unit::TestCase
@@ -121,8 +122,6 @@ class TestDateParse < Test::Unit::TestCase
[['S40.05.23T23:55:21-09:00',false],[1965,5,23,23,55,21,'-09:00',-9*3600,nil], __LINE__],
[['H11.05.23 23:55:21Z',false],[1999,5,23,23,55,21,'Z',0,nil], __LINE__],
[['H11.05.23T23:55:21Z',false],[1999,5,23,23,55,21,'Z',0,nil], __LINE__],
- [['H31.04.30 23:55:21Z',false],[2019,4,30,23,55,21,'Z',0,nil], __LINE__],
- [['H31.04.30T23:55:21Z',false],[2019,4,30,23,55,21,'Z',0,nil], __LINE__],
# ofx date
[['19990523235521',false],[1999,5,23,23,55,21,nil,nil,nil], __LINE__],
@@ -418,14 +417,7 @@ class TestDateParse < Test::Unit::TestCase
a[1] = -1
a[2] = h[:yday]
end
- l = format('<failed at line %d>', l)
- assert_equal(y, a, l)
- if y[6]
- h = Date._parse(x[0].dup.taint, *x[1..-1])
- assert_equal(y[6], h[:zone], l)
- assert_equal(y[6].encoding, h[:zone].encoding, l)
- assert_predicate(h[:zone], :tainted?, l)
- end
+ assert_equal(y, a, format('<failed at line %d>', l))
end
end
@@ -832,6 +824,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._iso8601('')
assert_equal({}, h)
+
+ h = Date._iso8601(nil)
+ assert_equal({}, h)
+
+ h = Date._iso8601('01-02-03T04:05:06Z'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__rfc3339
@@ -847,6 +846,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._rfc3339('')
assert_equal({}, h)
+
+ h = Date._rfc3339(nil)
+ assert_equal({}, h)
+
+ h = Date._rfc3339('2001-02-03T04:05:06Z'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__xmlschema
@@ -929,6 +935,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._xmlschema('')
assert_equal({}, h)
+
+ h = Date._xmlschema(nil)
+ assert_equal({}, h)
+
+ h = Date._xmlschema('2001-02-03'.to_sym)
+ assert_equal([2001, 2, 3, nil, nil, nil, nil],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__rfc2822
@@ -961,6 +974,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._rfc2822('')
assert_equal({}, h)
+
+ h = Date._rfc2822(nil)
+ assert_equal({}, h)
+
+ h = Date._rfc2822('Sat, 3 Feb 2001 04:05:06 UT'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__httpdate
@@ -981,6 +1001,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._httpdate('')
assert_equal({}, h)
+
+ h = Date._httpdate(nil)
+ assert_equal({}, h)
+
+ h = Date._httpdate('Sat, 03 Feb 2001 04:05:06 GMT'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 0],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test__jisx0301
@@ -1057,6 +1084,13 @@ class TestDateParse < Test::Unit::TestCase
h = Date._jisx0301('')
assert_equal({}, h)
+
+ h = Date._jisx0301(nil)
+ assert_equal({}, h)
+
+ h = Date._jisx0301('H13.02.03T04:05:06.07+0100'.to_sym)
+ assert_equal([2001, 2, 3, 4, 5, 6, 3600],
+ h.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset))
end
def test_iso8601
@@ -1201,16 +1235,34 @@ class TestDateParse < Test::Unit::TestCase
s0 = s.dup
assert_not_equal({}, Date._jisx0301(s))
assert_equal(s0, s)
-
- s = 'H31.04.30T04:05:06,07Z'
- s0 = s.dup
- assert_not_equal({}, Date._jisx0301(s))
- assert_equal(s0, s)
-
- s = 'H31.05.01T04:05:06,07Z'
- s0 = s.dup
- assert_not_equal({}, Date._jisx0301(s))
- assert_equal(s0, s)
end
+ def test_length_limit
+ assert_raise(ArgumentError) { Date._parse("1" * 1000) }
+ assert_raise(ArgumentError) { Date._iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { Date._xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { Date._rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { Date._jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { Date.parse("1" * 1000) }
+ assert_raise(ArgumentError) { Date.iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { Date.xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { Date.rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { Date.jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { DateTime.parse("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.iso8601("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc3339("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.xmlschema("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc2822("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.rfc822("1" * 1000) }
+ assert_raise(ArgumentError) { DateTime.jisx0301("1" * 1000) }
+
+ assert_raise(ArgumentError) { Date._parse("Jan " + "9" * 1000000) }
+ assert_raise(Timeout::Error) { Timeout.timeout(1) { Date._parse("Jan " + "9" * 1000000, limit: nil) } }
+ end
end
diff --git a/test/net/ftp/test_ftp.rb b/test/net/ftp/test_ftp.rb
index a5219644bb..b3fe7774ed 100644
--- a/test/net/ftp/test_ftp.rb
+++ b/test/net/ftp/test_ftp.rb
@@ -61,7 +61,7 @@ class FTPTest < Test::Unit::TestCase
end
def test_parse227
- ftp = Net::FTP.new
+ ftp = Net::FTP.new(nil, use_pasv_ip: true)
host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
assert_equal("192.168.0.1", host)
assert_equal(3106, port)
@@ -80,6 +80,14 @@ class FTPTest < Test::Unit::TestCase
assert_raise(Net::FTPProtoError) do
ftp.send(:parse227, "227 ) foo bar (")
end
+
+ ftp = Net::FTP.new
+ sock = OpenStruct.new
+ sock.remote_address = OpenStruct.new
+ sock.remote_address.ip_address = "10.0.0.1"
+ ftp.instance_variable_set(:@bare_sock, sock)
+ host, port = ftp.send(:parse227, "227 Entering Passive Mode (192,168,0,1,12,34)")
+ assert_equal("10.0.0.1", host)
end
def test_parse228
@@ -2360,10 +2368,155 @@ EOF
end
end
+ def test_ignore_pasv_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ data_server = TCPServer.new("127.0.0.1", 0)
+ port = data_server.local_address.ip_port
+ sock.printf("227 Entering Passive Mode (999,0,0,1,%s).\r\n",
+ port.divmod(256).join(","))
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = data_server.accept
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ data_server.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_equal("PASV\r\n", commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_use_pasv_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ data_server = TCPServer.new("127.0.0.1", 0)
+ port = data_server.local_address.ip_port
+ sock.printf("227 Entering Passive Mode (127,0,0,1,%s).\r\n",
+ port.divmod(256).join(","))
+ commands.push(sock.gets)
+ sock.print("150 Opening BINARY mode data connection for foo (#{binary_data.size} bytes)\r\n")
+ conn = data_server.accept
+ binary_data.scan(/.{1,1024}/nm) do |s|
+ conn.print(s)
+ end
+ conn.shutdown(Socket::SHUT_WR)
+ conn.read
+ conn.close
+ data_server.close
+ sock.print("226 Transfer complete.\r\n")
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.use_pasv_ip = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ buf = ftp.getbinaryfile("foo", nil)
+ assert_equal(binary_data, buf)
+ assert_equal(Encoding::ASCII_8BIT, buf.encoding)
+ assert_equal("PASV\r\n", commands.shift)
+ assert_equal("RETR foo\r\n", commands.shift)
+ assert_equal(nil, commands.shift)
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
+ def test_use_pasv_invalid_ip
+ commands = []
+ binary_data = (0..0xff).map {|i| i.chr}.join * 4 * 3
+ server = create_ftp_server(nil, "127.0.0.1") { |sock|
+ sock.print("220 (test_ftp).\r\n")
+ commands.push(sock.gets)
+ sock.print("331 Please specify the password.\r\n")
+ commands.push(sock.gets)
+ sock.print("230 Login successful.\r\n")
+ commands.push(sock.gets)
+ sock.print("200 Switching to Binary mode.\r\n")
+ line = sock.gets
+ commands.push(line)
+ sock.print("227 Entering Passive Mode (999,0,0,1,48,57).\r\n")
+ commands.push(sock.gets)
+ }
+ begin
+ begin
+ ftp = Net::FTP.new
+ ftp.passive = true
+ ftp.use_pasv_ip = true
+ ftp.read_timeout *= 5 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # for --jit-wait
+ ftp.connect("127.0.0.1", server.port)
+ ftp.login
+ assert_match(/\AUSER /, commands.shift)
+ assert_match(/\APASS /, commands.shift)
+ assert_equal("TYPE I\r\n", commands.shift)
+ assert_raise(SocketError) do
+ ftp.getbinaryfile("foo", nil)
+ end
+ ensure
+ ftp.close if ftp
+ end
+ ensure
+ server.close
+ end
+ end
+
private
- def create_ftp_server(sleep_time = nil)
- server = TCPServer.new(SERVER_ADDR, 0)
+ def create_ftp_server(sleep_time = nil, addr = SERVER_ADDR)
+ server = TCPServer.new(addr, 0)
@thread = Thread.start do
if sleep_time
sleep(sleep_time)
diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb
index c1d486470a..784f002c22 100644
--- a/test/net/http/test_https.rb
+++ b/test/net/http/test_https.rb
@@ -44,8 +44,10 @@ class TestNetHTTPS < Test::Unit::TestCase
http.request_get("/") {|res|
assert_equal($test_net_http_data, res.body)
}
- assert_equal(CA_CERT.to_der, certs[0].to_der)
- assert_equal(SERVER_CERT.to_der, certs[1].to_der)
+ # TODO: OpenSSL 1.1.1h seems to yield only SERVER_CERT; need to check the incompatibility
+ certs.zip([CA_CERT, SERVER_CERT]) do |actual, expected|
+ assert_equal(expected.to_der, actual.to_der)
+ end
rescue SystemCallError
skip $!
end
diff --git a/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb
index 936f4e0f42..81928cb8fe 100644
--- a/test/net/imap/test_imap.rb
+++ b/test/net/imap/test_imap.rb
@@ -127,6 +127,24 @@ class IMAPTest < Test::Unit::TestCase
imap.disconnect
end
end
+
+ def test_starttls_stripping
+ starttls_stripping_test do |port|
+ imap = Net::IMAP.new("localhost", :port => port)
+ assert_raise(Net::IMAP::UnknownResponseError) do
+ imap.starttls(:ca_file => CA_FILE)
+ end
+ imap
+ end
+ end
+ end
+
+ def start_server
+ th = Thread.new do
+ yield
+ end
+ @threads << th
+ sleep 0.1 until th.stop?
end
def test_unexpected_eof
@@ -762,6 +780,27 @@ EOF
end
end
+ def starttls_stripping_test
+ server = create_tcp_server
+ port = server.addr[1]
+ start_server do
+ sock = server.accept
+ begin
+ sock.print("* OK test server\r\n")
+ sock.gets
+ sock.print("RUBY0001 BUG unhandled command\r\n")
+ ensure
+ sock.close
+ server.close
+ end
+ end
+ begin
+ imap = yield(port)
+ ensure
+ imap.disconnect if imap && !imap.disconnected?
+ end
+ end
+
def create_tcp_server
return TCPServer.new(server_addr, 0)
end
diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb
index 947170203a..f139b90efe 100644
--- a/test/objspace/test_objspace.rb
+++ b/test/objspace/test_objspace.rb
@@ -98,6 +98,8 @@ class TestObjSpace < Test::Unit::TestCase
res = ObjectSpace.count_imemo_objects
assert_not_empty(res)
assert_not_nil(res[:imemo_cref])
+ assert_not_empty res.inspect
+
arg = {}
res = ObjectSpace.count_imemo_objects(arg)
assert_not_empty(res)
diff --git a/test/pathname/test_pathname.rb b/test/pathname/test_pathname.rb
index f8e4937802..750fabf039 100644
--- a/test/pathname/test_pathname.rb
+++ b/test/pathname/test_pathname.rb
@@ -824,7 +824,7 @@ class TestPathname < Test::Unit::TestCase
old = path.lstat.mode
begin
path.lchmod(0444)
- rescue NotImplementedError
+ rescue NotImplementedError, Errno::EOPNOTSUPP
next
end
assert_equal(0444, path.lstat.mode & 0777)
diff --git a/test/rdoc/test_rdoc_rdoc.rb b/test/rdoc/test_rdoc_rdoc.rb
index 3bce54b243..123b1a4f87 100644
--- a/test/rdoc/test_rdoc_rdoc.rb
+++ b/test/rdoc/test_rdoc_rdoc.rb
@@ -420,6 +420,18 @@ class TestRDocRDoc < RDoc::TestCase
end
end
+ def test_remove_unparseable_CVE_2021_31799
+ skip 'for Un*x platforms' if Gem.win_platform?
+ temp_dir do
+ file_list = ['| touch evil.txt && echo tags']
+ file_list.each do |f|
+ FileUtils.touch f
+ end
+ assert_equal file_list, @rdoc.remove_unparseable(file_list)
+ assert_equal file_list, Dir.children('.')
+ end
+ end
+
def test_setup_output_dir
Dir.mktmpdir {|d|
path = File.join d, 'testdir'
diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb
index 669d86bd83..c49c152208 100644
--- a/test/resolv/test_dns.rb
+++ b/test/resolv/test_dns.rb
@@ -128,6 +128,119 @@ class TestResolvDNS < Test::Unit::TestCase
}
end
+ def test_query_ipv4_duplicate_responses
+ begin
+ OpenSSL
+ rescue LoadError
+ skip 'autoload problem. see [ruby-dev:45021][Bug #5786]'
+ end if defined?(OpenSSL)
+
+ with_udp('127.0.0.1', 0) {|u|
+ _, server_port, _, server_address = u.addr
+ begin
+ client_thread = Thread.new {
+ Resolv::DNS.open(:nameserver_port => [[server_address, server_port]], :search => ['bad1.com', 'bad2.com', 'good.com'], ndots: 5) {|dns|
+ dns.getaddress("example")
+ }
+ }
+ server_thread = Thread.new {
+ 3.times do
+ msg, (_, client_port, _, client_address) = Timeout.timeout(5) {u.recvfrom(4096)}
+ id, flags, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn")
+
+ qr = (flags & 0x8000) >> 15
+ opcode = (flags & 0x7800) >> 11
+ aa = (flags & 0x0400) >> 10
+ tc = (flags & 0x0200) >> 9
+ rd = (flags & 0x0100) >> 8
+ ra = (flags & 0x0080) >> 7
+ z = (flags & 0x0070) >> 4
+ rcode = flags & 0x000f
+ rest = msg[12..-1]
+
+ questions = msg.bytes[12..-1]
+ labels = []
+ idx = 0
+ while idx < questions.length-5
+ size = questions[idx]
+ labels << questions[idx+1..idx+size].pack('c*')
+ idx += size+1
+ end
+ hostname = labels.join('.')
+
+ if hostname == "example.good.com"
+ id = id
+ qr = 1
+ opcode = opcode
+ aa = 0
+ tc = 0
+ rd = rd
+ ra = 1
+ z = 0
+ rcode = 0
+ qdcount = 1
+ ancount = 1
+ nscount = 0
+ arcount = 0
+ word2 = (qr << 15) |
+ (opcode << 11) |
+ (aa << 10) |
+ (tc << 9) |
+ (rd << 8) |
+ (ra << 7) |
+ (z << 4) |
+ rcode
+ msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
+ msg << questions.pack('c*')
+ type = 1
+ klass = 1
+ ttl = 3600
+ rdlength = 4
+ rdata = [52,0,2,1].pack("CCCC")
+ rr = [0xc00c, type, klass, ttl, rdlength, rdata].pack("nnnNna*")
+ msg << rr
+ rdata = [52,0,2,2].pack("CCCC")
+ rr = [0xc00c, type, klass, ttl, rdlength, rdata].pack("nnnNna*")
+ msg << rr
+
+ u.send(msg, 0, client_address, client_port)
+ else
+ id = id
+ qr = 1
+ opcode = opcode
+ aa = 0
+ tc = 0
+ rd = rd
+ ra = 1
+ z = 0
+ rcode = 3
+ qdcount = 1
+ ancount = 0
+ nscount = 0
+ arcount = 0
+ word2 = (qr << 15) |
+ (opcode << 11) |
+ (aa << 10) |
+ (tc << 9) |
+ (rd << 8) |
+ (ra << 7) |
+ (z << 4) |
+ rcode
+ msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
+ msg << questions.pack('c*')
+
+ u.send(msg, 0, client_address, client_port)
+ u.send(msg, 0, client_address, client_port)
+ end
+ end
+ }
+ result, _ = assert_join_threads([client_thread, server_thread])
+ assert_instance_of(Resolv::IPv4, result)
+ assert_equal("52.0.2.1", result.to_s)
+ end
+ }
+ end
+
def test_query_ipv4_address_timeout
with_udp('127.0.0.1', 0) {|u|
_, port , _, host = u.addr
diff --git a/test/rexml/parse/test_document_type_declaration.rb b/test/rexml/parse/test_document_type_declaration.rb
index 80f70888fb..55713909e7 100644
--- a/test/rexml/parse/test_document_type_declaration.rb
+++ b/test/rexml/parse/test_document_type_declaration.rb
@@ -5,17 +5,187 @@ require "rexml/document"
module REXMLTests
class TestParseDocumentTypeDeclaration < Test::Unit::TestCase
private
- def xml(internal_subset)
- <<-XML
-<!DOCTYPE r SYSTEM "urn:x-rexml:test" [
-#{internal_subset}
-]>
+ def parse(doctype)
+ REXML::Document.new(<<-XML).doctype
+#{doctype}
<r/>
XML
end
- def parse(internal_subset)
- REXML::Document.new(xml(internal_subset)).doctype
+ class TestName < self
+ def test_valid
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r>
+ DOCTYPE
+ assert_equal("r", doctype.name)
+ end
+
+ def test_garbage_plus_before_name_at_line_start
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE +
+r SYSTEM "urn:x-rexml:test" [
+]>
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: invalid name
+Line: 5
+Position: 51
+Last 80 unconsumed characters:
++ r SYSTEM "urn:x-rexml:test" [ ]> <r/>
+ DETAIL
+ end
+ end
+
+ class TestExternalID < self
+ class TestSystem < self
+ def test_left_bracket_in_system_literal
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM "urn:x-rexml:[test" [
+]>
+ DOCTYPE
+ assert_equal([
+ "r",
+ "SYSTEM",
+ nil,
+ "urn:x-rexml:[test",
+ ],
+ [
+ doctype.name,
+ doctype.external_id,
+ doctype.public,
+ doctype.system,
+ ])
+ end
+
+ def test_greater_than_in_system_literal
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM "urn:x-rexml:>test" [
+]>
+ DOCTYPE
+ assert_equal([
+ "r",
+ "SYSTEM",
+ nil,
+ "urn:x-rexml:>test",
+ ],
+ [
+ doctype.name,
+ doctype.external_id,
+ doctype.public,
+ doctype.system,
+ ])
+ end
+
+ def test_no_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM>
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: system literal is missing
+Line: 3
+Position: 26
+Last 80 unconsumed characters:
+ SYSTEM> <r/>
+ DETAIL
+ end
+
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM 'r.dtd'x'>
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: garbage after external ID
+Line: 3
+Position: 36
+Last 80 unconsumed characters:
+x'> <r/>
+ DETAIL
+ end
+
+ def test_single_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM 'r".dtd'>
+ DOCTYPE
+ assert_equal("r\".dtd", doctype.system)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM "r'.dtd">
+ DOCTYPE
+ assert_equal("r'.dtd", doctype.system)
+ end
+ end
+
+ class TestPublic < self
+ class TestPublicIDLiteral < self
+ def test_content_double_quote
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC 'double quote " is invalid' "r.dtd">
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: invalid public ID literal
+Line: 3
+Position: 62
+Last 80 unconsumed characters:
+ PUBLIC 'double quote " is invalid' "r.dtd"> <r/>
+ DETAIL
+ end
+
+ def test_single_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC 'public-id-literal' "r.dtd">
+ DOCTYPE
+ assert_equal("public-id-literal", doctype.public)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC "public'-id-literal" "r.dtd">
+ DOCTYPE
+ assert_equal("public'-id-literal", doctype.public)
+ end
+ end
+
+ class TestSystemLiteral < self
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC 'public-id-literal' 'system-literal'x'>
+ DOCTYPE
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed DOCTYPE: garbage after external ID
+Line: 3
+Position: 65
+Last 80 unconsumed characters:
+x'> <r/>
+ DETAIL
+ end
+
+ def test_single_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC "public-id-literal" 'system"-literal'>
+ DOCTYPE
+ assert_equal("system\"-literal", doctype.system)
+ end
+
+ def test_double_quote
+ doctype = parse(<<-DOCTYPE)
+<!DOCTYPE r PUBLIC "public-id-literal" "system'-literal">
+ DOCTYPE
+ assert_equal("system'-literal", doctype.system)
+ end
+ end
+ end
end
class TestMixed < self
@@ -45,6 +215,15 @@ module REXMLTests
assert_equal([REXML::NotationDecl, REXML::AttlistDecl],
doctype.children.collect(&:class))
end
+
+ private
+ def parse(internal_subset)
+ super(<<-DOCTYPE)
+<!DOCTYPE r SYSTEM "urn:x-rexml:test" [
+#{internal_subset}
+]>
+ DOCTYPE
+ end
end
end
end
diff --git a/test/rexml/parse/test_element.rb b/test/rexml/parse/test_element.rb
index aad915fe7b..7206fe595a 100644
--- a/test/rexml/parse/test_element.rb
+++ b/test/rexml/parse/test_element.rb
@@ -33,6 +33,32 @@ Last 80 unconsumed characters:
DETAIL
end
+
+ def test_garbage_less_than_before_root_element_at_line_start
+ exception = assert_raise(REXML::ParseException) do
+ parse("<\n<x/>")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+malformed XML: missing tag start
+Line: 2
+Position: 6
+Last 80 unconsumed characters:
+< <x/>
+ DETAIL
+ end
+
+ def test_garbage_less_than_slash_before_end_tag_at_line_start
+ exception = assert_raise(REXML::ParseException) do
+ parse("<x></\n</x>")
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Missing end tag for 'x'
+Line: 2
+Position: 10
+Last 80 unconsumed characters:
+</ </x>
+ DETAIL
+ end
end
end
end
diff --git a/test/rexml/parse/test_notation_declaration.rb b/test/rexml/parse/test_notation_declaration.rb
index 0d29f0d81f..19a0536d0a 100644
--- a/test/rexml/parse/test_notation_declaration.rb
+++ b/test/rexml/parse/test_notation_declaration.rb
@@ -23,10 +23,100 @@ module REXMLTests
doctype = parse("<!NOTATION name PUBLIC 'urn:public-id'>")
assert_equal("name", doctype.notation("name").name)
end
+
+ def test_no_name
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: name is missing
+Line: 5
+Position: 72
+Last 80 unconsumed characters:
+ <!NOTATION> ]> <r/>
+ DETAIL
+ end
+
+ def test_invalid_name
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION '>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid name
+Line: 5
+Position: 74
+Last 80 unconsumed characters:
+'> ]> <r/>
+ DETAIL
+ end
+
+ def test_no_id_type
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid ID type
+Line: 5
+Position: 77
+Last 80 unconsumed characters:
+> ]> <r/>
+ DETAIL
+ end
+
+ def test_invalid_id_type
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name INVALID>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid ID type
+Line: 5
+Position: 85
+Last 80 unconsumed characters:
+ INVALID> ]> <r/>
+ DETAIL
+ end
end
class TestExternalID < self
class TestSystem < self
+ def test_no_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name SYSTEM>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: system literal is missing
+Line: 5
+Position: 84
+Last 80 unconsumed characters:
+ SYSTEM> ]> <r/>
+ DETAIL
+ end
+
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name SYSTEM 'system-literal'x'>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: garbage before end >
+Line: 5
+Position: 103
+Last 80 unconsumed characters:
+x'> ]> <r/>
+ DETAIL
+ end
+
def test_single_quote
doctype = parse(<<-INTERNAL_SUBSET)
<!NOTATION name SYSTEM 'system-literal'>
@@ -44,6 +134,21 @@ module REXMLTests
class TestPublic < self
class TestPublicIDLiteral < self
+ def test_content_double_quote
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'double quote " is invalid' "system-literal">
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid public ID literal
+Line: 5
+Position: 129
+Last 80 unconsumed characters:
+ PUBLIC 'double quote " is invalid' "system-literal"> ]> <r/>
+ DETAIL
+ end
+
def test_single_quote
doctype = parse(<<-INTERNAL_SUBSET)
<!NOTATION name PUBLIC 'public-id-literal' "system-literal">
@@ -60,6 +165,21 @@ module REXMLTests
end
class TestSystemLiteral < self
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'public-id-literal' 'system-literal'x'>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: garbage before end >
+Line: 5
+Position: 123
+Last 80 unconsumed characters:
+x'> ]> <r/>
+ DETAIL
+ end
+
def test_single_quote
doctype = parse(<<-INTERNAL_SUBSET)
<!NOTATION name PUBLIC "public-id-literal" 'system-literal'>
@@ -96,5 +216,66 @@ module REXMLTests
end
end
end
+
+ class TestPublicID < self
+ def test_no_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: public ID literal is missing
+Line: 5
+Position: 84
+Last 80 unconsumed characters:
+ PUBLIC> ]> <r/>
+ DETAIL
+ end
+
+ def test_literal_content_double_quote
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'double quote " is invalid in PubidLiteral'>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: invalid public ID literal
+Line: 5
+Position: 128
+Last 80 unconsumed characters:
+ PUBLIC 'double quote \" is invalid in PubidLiteral'> ]> <r/>
+ DETAIL
+ end
+
+ def test_garbage_after_literal
+ exception = assert_raise(REXML::ParseException) do
+ parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'public-id-literal'x'>
+ INTERNAL_SUBSET
+ end
+ assert_equal(<<-DETAIL.chomp, exception.to_s)
+Malformed notation declaration: garbage before end >
+Line: 5
+Position: 106
+Last 80 unconsumed characters:
+x'> ]> <r/>
+ DETAIL
+ end
+
+ def test_literal_single_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC 'public-id-literal'>
+ INTERNAL_SUBSET
+ assert_equal("public-id-literal", doctype.notation("name").public)
+ end
+
+ def test_literal_double_quote
+ doctype = parse(<<-INTERNAL_SUBSET)
+<!NOTATION name PUBLIC "public-id-literal">
+ INTERNAL_SUBSET
+ assert_equal("public-id-literal", doctype.notation("name").public)
+ end
+ end
end
end
diff --git a/test/rexml/parse/test_processing_instruction.rb b/test/rexml/parse/test_processing_instruction.rb
index a23513fc6e..f0c0c24e67 100644
--- a/test/rexml/parse/test_processing_instruction.rb
+++ b/test/rexml/parse/test_processing_instruction.rb
@@ -20,6 +20,25 @@ Last 80 unconsumed characters:
<??>
DETAIL
end
+
+ def test_garbage_text
+ # TODO: This should be parse error.
+ # Create test/parse/test_document.rb or something and move this to it.
+ doc = parse(<<-XML)
+x<?x y
+<!--?><?x -->?>
+<r/>
+ XML
+ pi = doc.children[1]
+ assert_equal([
+ "x",
+ "y\n<!--",
+ ],
+ [
+ pi.target,
+ pi.content,
+ ])
+ end
end
end
end
diff --git a/test/rexml/parser/test_ultra_light.rb b/test/rexml/parser/test_ultra_light.rb
index 8f4a3980d5..44fd1d1ec0 100644
--- a/test/rexml/parser/test_ultra_light.rb
+++ b/test/rexml/parser/test_ultra_light.rb
@@ -16,7 +16,6 @@ class TestUltraLightParser < Test::Unit::TestCase
nil,
[:entitydecl, "name", "value"]
],
- [:text, "\n"],
[:start_element, :parent, "root", {}],
[:text, "\n"],
],
diff --git a/test/rexml/test_core.rb b/test/rexml/test_core.rb
index 46036d7f12..ae528d75d3 100644
--- a/test/rexml/test_core.rb
+++ b/test/rexml/test_core.rb
@@ -1,4 +1,4 @@
-# coding: binary
+# coding: utf-8
# frozen_string_literal: false
require_relative "rexml_test_utils"
@@ -995,7 +995,7 @@ EOL
document.write(s)
## XML Doctype
- str = '<!DOCTYPE foo "bar">'
+ str = '<!DOCTYPE foo SYSTEM "bar">'
source = REXML::Source.new(str)
doctype = REXML::DocType.new(source)
document.add(doctype)
diff --git a/test/rexml/test_doctype.rb b/test/rexml/test_doctype.rb
index 7f42669170..915717de8e 100644
--- a/test/rexml/test_doctype.rb
+++ b/test/rexml/test_doctype.rb
@@ -18,12 +18,6 @@ module REXMLTests
@doc_type_system = REXML::Document.new(xml_system).doctype
@pubid = "TEST_ID"
- xml_public = <<-XML
- <!DOCTYPE root PUBLIC "#{@pubid}">
- <root/>
- XML
- @doc_type_public = REXML::Document.new(xml_public).doctype
-
xml_public_system = <<-XML
<!DOCTYPE root PUBLIC "#{@pubid}" "#{@sysid}">
<root/>
@@ -35,11 +29,9 @@ module REXMLTests
assert_equal([
nil,
@pubid,
- @pubid,
],
[
@doc_type_system.public,
- @doc_type_public.public,
@doc_type_public_system.public,
])
end
@@ -58,12 +50,10 @@ module REXMLTests
def test_system
assert_equal([
@sysid,
- nil,
@sysid,
],
[
@doc_type_system.system,
- @doc_type_public.system,
@doc_type_public_system.system,
])
end
diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index 95ec661fcf..1f2f960020 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -1498,4 +1498,10 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
assert_warn("") {fmt, = warn("\r;")}
assert_match(/encountered/, fmt)
end
+
+ def test_warn_mismatched_indentations
+ fmt, tokend, tokbeg, line = assert_warning("") {break warn("if true\n end\n")}
+ assert_match(/mismatched indentations/, fmt)
+ assert_equal(["if", "end", 1], [tokbeg, tokend, line])
+ end
end if ripper_test
diff --git a/test/ruby/test_arithmetic_sequence.rb b/test/ruby/test_arithmetic_sequence.rb
index 88fd3136cc..143906f4e2 100644
--- a/test/ruby/test_arithmetic_sequence.rb
+++ b/test/ruby/test_arithmetic_sequence.rb
@@ -254,6 +254,11 @@ class TestArithmeticSequence < Test::Unit::TestCase
'[ruby-core:90648] [Bug #15444]')
end
+ def test_last_bug17218
+ seq = (1.0997r .. 1.1r).step(0.0001r)
+ assert_equal([1.0997r, 1.0998r, 1.0999r, 1.1r], seq.to_a, '[ruby-core:100312] [Bug #17218]')
+ end
+
def test_slice
seq = 1.step(10, 2)
assert_equal([[1, 3, 5], [7, 9]], seq.each_slice(3).to_a)
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index 1a897ef502..3ba67e69a2 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -269,4 +269,15 @@ class TestAst < Test::Unit::TestCase
assert_equal(:LIT, body.type)
assert_equal([1], body.children)
end
+
+ def test_op_asgn2
+ node = RubyVM::AbstractSyntaxTree.parse("struct.field += foo")
+ _, _, body = *node.children
+ assert_equal(:OP_ASGN2, body.type)
+ recv, _, mid, op, value = body.children
+ assert_equal(:VCALL, recv.type)
+ assert_equal(:field, mid)
+ assert_equal(:+, op)
+ assert_equal(:VCALL, value.type)
+ end
end
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index ad2caeb8be..2ab1e7f266 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -232,6 +232,11 @@ class TestClass < Test::Unit::TestCase
assert_raise(TypeError) { Class.allocate.superclass }
bug6863 = '[ruby-core:47148]'
assert_raise(TypeError, bug6863) { Class.new(Class.allocate) }
+
+ allocator = Class.instance_method(:allocate)
+ assert_raise_with_message(TypeError, /prohibited/) {
+ allocator.bind(Rational).call
+ }
end
def test_nonascii_name
@@ -427,6 +432,53 @@ class TestClass < Test::Unit::TestCase
assert_equal(:foo, d.foo)
end
+ def test_clone_singleton_class_exists
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_empty(o.singleton_class.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
+ def test_clone_when_singleton_class_of_singleton_class_exists
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class.singleton_class
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_empty(o.singleton_class.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
+ def test_clone_when_method_exists_on_singleton_class_of_singleton_class
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class.singleton_class.define_method(:s2_method) { :s2 }
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_equal(:s2, o.singleton_class.s2_method)
+ assert_equal(:s2, clone.singleton_class.s2_method)
+ assert_equal([:s2_method], o.singleton_class.singleton_class.instance_methods(false))
+ assert_equal([:s2_method], clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
def test_singleton_class_p
feature7609 = '[ruby-core:51087] [Feature #7609]'
assert_predicate(self.singleton_class, :singleton_class?, feature7609)
diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb
index 9976db3b6f..e1571d5714 100644
--- a/test/ruby/test_defined.rb
+++ b/test/ruby/test_defined.rb
@@ -23,40 +23,80 @@ class TestDefined < Test::Unit::TestCase
return !defined?(yield)
end
- def test_defined
+ def test_defined_global_variable
$x = nil
assert(defined?($x)) # global variable
assert_equal('global-variable', defined?($x))# returns description
+ end
+ def test_defined_local_variable
assert_nil(defined?(foo)) # undefined
foo=5
assert(defined?(foo)) # local variable
+ end
+ def test_defined_constant
assert(defined?(Array)) # constant
assert(defined?(::Array)) # toplevel constant
assert(defined?(File::Constants)) # nested constant
+ end
+
+ def test_defined_public_method
assert(defined?(Object.new)) # method
assert(defined?(Object::new)) # method
+ end
+
+ def test_defined_private_method
assert(!defined?(Object.print)) # private method
+ end
+
+ def test_defined_operator
assert(defined?(1 == 2)) # operator expression
+ end
+ def test_defined_protected_method
f = Foo.new
assert_nil(defined?(f.foo)) # protected method
f.bar(f) { |v| assert(v) }
+ f.bar(Class.new(Foo).new) { |v| assert(v, "inherited protected method") }
+ end
+
+ def test_defined_undefined_method
+ f = Foo.new
assert_nil(defined?(f.quux)) # undefined method
+ end
+
+ def test_defined_undefined_argument
+ f = Foo.new
assert_nil(defined?(f.baz(x))) # undefined argument
x = 0
assert(defined?(f.baz(x)))
assert_nil(defined?(f.quux(x)))
assert(defined?(print(x)))
assert_nil(defined?(quux(x)))
+ end
+
+ def test_defined_attrasgn
+ f = Foo.new
assert(defined?(f.attr = 1))
f.attrasgn_test { |v| assert(v) }
+ end
+
+ def test_defined_undef
+ x = Object.new
+ def x.foo; end
+ assert(defined?(x.foo))
+ x.instance_eval {undef :foo}
+ assert(!defined?(x.foo), "undefed method should not be defined?")
+ end
+ def test_defined_yield
assert(defined_test) # not iterator
assert(!defined_test{}) # called as iterator
+ end
+ def test_defined_matchdata
/a/ =~ ''
assert_equal nil, defined?($&)
assert_equal nil, defined?($`)
@@ -85,12 +125,16 @@ class TestDefined < Test::Unit::TestCase
assert_equal 'global-variable', defined?($+)
assert_equal 'global-variable', defined?($1)
assert_equal nil, defined?($2)
+ end
+ def test_defined_literal
assert_equal("nil", defined?(nil))
assert_equal("true", defined?(true))
assert_equal("false", defined?(false))
assert_equal("expression", defined?(1))
+ end
+ def test_defined_empty_paren_expr
bug8224 = '[ruby-core:54024] [Bug #8224]'
(1..3).each do |level|
expr = "("*level+")"*level
diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb
index 6343642ac1..e9fb2524f6 100644
--- a/test/ruby/test_env.rb
+++ b/test/ruby/test_env.rb
@@ -440,6 +440,8 @@ class TestEnv < Test::Unit::TestCase
ENV["foo"] = "xxx"
ENV.replace({"foo"=>"bar", "baz"=>"qux"})
check(ENV.to_hash.to_a, [%w(foo bar), %w(baz qux)])
+ ENV.replace({"Foo"=>"Bar", "Baz"=>"Qux"})
+ check(ENV.to_hash.to_a, [%w(Foo Bar), %w(Baz Qux)])
end
def test_update
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 05c74f6dcd..de249c9b7c 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -842,6 +842,26 @@ end.join
}
end
+ def test_cause_exception_in_cause_message
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}") do |outs, errs, status|
+ begin;
+ exc = Class.new(StandardError) do
+ def initialize(obj, cnt)
+ super(obj)
+ @errcnt = cnt
+ end
+ def to_s
+ return super if @errcnt <= 0
+ @errcnt -= 1
+ raise "xxx"
+ end
+ end.new("ok", 10)
+ raise "[Bug #17033]", cause: exc
+ end;
+ assert_equal(1, errs.count {|m| m.include?("[Bug #17033]")}, proc {errs.pretty_inspect})
+ end
+ end
+
def test_anonymous_message
assert_in_out_err([], "raise Class.new(RuntimeError), 'foo'", [], /foo\n/)
end
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index 3453440694..0b2e4df05b 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -171,6 +171,24 @@ class TestFloat < Test::Unit::TestCase
assert_raise(ArgumentError, n += z + "A") {Float(n)}
assert_raise(ArgumentError, n += z + ".0") {Float(n)}
end
+
+ x = nil
+ 2000.times do
+ x = Float("0x"+"0"*30)
+ break unless x == 0.0
+ end
+ assert_equal(0.0, x, ->{"%a" % x})
+ x = nil
+ 2000.times do
+ begin
+ x = Float("0x1."+"0"*270)
+ rescue ArgumentError => e
+ raise unless /"0x1\.0{270}"/ =~ e.message
+ else
+ break
+ end
+ end
+ assert_nil(x, ->{"%a" % x})
end
def test_divmod
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index 69347b6b11..0f289d8ed9 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -595,6 +595,9 @@ class TestInteger < Test::Unit::TestCase
failures << n unless root*root <= n && (root+1)*(root+1) > n
end
assert_empty(failures, bug13440)
+
+ x = 0xffff_ffff_ffff_ffff
+ assert_equal(x, Integer.sqrt(x ** 2), "[ruby-core:95453]")
end
def test_fdiv
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index 84a5cfec66..8e2da53bdf 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -520,12 +520,6 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(:ok, m.f(*a, **o), '[ruby-core:83638] [Bug #10856]')
o = {a: 42}
- assert_warning(/splat keyword/, 'splat to mandatory') do
- assert_equal({a: 42}, m.f1(**o))
- end
- assert_warning(/splat keyword/) do
- assert_equal({a: 42}, m.f2(**o), '[ruby-core:82280] [Bug #13791]')
- end
assert_warning('', 'splat to kwrest') do
assert_equal({a: 42}, m.f3(**o))
end
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb
index 3a7186584a..3bf25928c9 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -458,6 +458,14 @@ class TestMethod < Test::Unit::TestCase
m.taint
assert_predicate(m.inspect, :tainted?, "inspect result should be infected")
+
+ bug15608 = '[ruby-core:91570] [Bug #15608]'
+ c4 = Class.new(c)
+ c4.class_eval { alias bar foo }
+ o = c4.new
+ o.singleton_class
+ m4 = o.method(:bar)
+ assert_equal("#<Method: #{c4.inspect}(#{c.inspect})#bar(foo)>", m4.inspect, bug15608)
end
def test_callee_top_level
@@ -950,6 +958,99 @@ class TestMethod < Test::Unit::TestCase
'[ruby-core:85231] [Bug #14421]'
end
+ def test_super_method_alias
+ c0 = Class.new do
+ def m1
+ [:C0_m1]
+ end
+ def m2
+ [:C0_m2]
+ end
+ end
+
+ c1 = Class.new(c0) do
+ def m1
+ [:C1_m1] + super
+ end
+ alias m2 m1
+ end
+
+ c2 = Class.new(c1) do
+ def m2
+ [:C2_m2] + super
+ end
+ end
+ o1 = c2.new
+ assert_equal([:C2_m2, :C1_m1, :C0_m1], o1.m2)
+
+ m = o1.method(:m2)
+ assert_equal([:C2_m2, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C0_m1], m.call)
+
+ assert_nil(m.super_method)
+ end
+
+ def test_super_method_alias_to_prepended_module
+ m = Module.new do
+ def m1
+ [:P_m1] + super
+ end
+
+ def m2
+ [:P_m2] + super
+ end
+ end
+
+ c0 = Class.new do
+ def m1
+ [:C0_m1]
+ end
+ end
+
+ c1 = Class.new(c0) do
+ def m1
+ [:C1_m1] + super
+ end
+ prepend m
+ alias m2 m1
+ end
+
+ o1 = c1.new
+ assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], o1.m2)
+
+ m = o1.method(:m2)
+ assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:P_m1, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C0_m1], m.call)
+
+ assert_nil(m.super_method)
+ end
+
+ # Bug 17780
+ def test_super_method_module_alias
+ m = Module.new do
+ def foo
+ end
+ alias :f :foo
+ end
+
+ method = m.instance_method(:f)
+ super_method = method.super_method
+ assert_nil(super_method)
+ end
+
def rest_parameter(*rest)
rest
end
diff --git a/test/ruby/test_notimp.rb b/test/ruby/test_notimp.rb
index ddebb657bf..daa5a82d7b 100644
--- a/test/ruby/test_notimp.rb
+++ b/test/ruby/test_notimp.rb
@@ -13,11 +13,11 @@ class TestNotImplement < Test::Unit::TestCase
def test_respond_to_lchmod
assert_include(File.methods, :lchmod)
- if /linux/ =~ RUBY_PLATFORM
- assert_equal(false, File.respond_to?(:lchmod))
- end
- if /freebsd/ =~ RUBY_PLATFORM
+ case RUBY_PLATFORM
+ when /freebsd/, /linux-musl/
assert_equal(true, File.respond_to?(:lchmod))
+ when /linux/
+ assert_equal(false, File.respond_to?(:lchmod))
end
end
@@ -57,9 +57,14 @@ class TestNotImplement < Test::Unit::TestCase
File.open(f, "w") {}
File.symlink f, g
newmode = 0444
- File.lchmod newmode, "#{d}/g"
- snew = File.lstat(g)
- assert_equal(newmode, snew.mode & 0777)
+ begin
+ File.lchmod newmode, "#{d}/g"
+ rescue Errno::EOPNOTSUPP
+ skip $!
+ else
+ snew = File.lstat(g)
+ assert_equal(newmode, snew.mode & 0777)
+ end
}
end
end
diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb
index a8cdf22a49..2957e1bfc8 100644
--- a/test/ruby/test_rational.rb
+++ b/test/ruby/test_rational.rb
@@ -598,6 +598,13 @@ class Rational_Test < Test::Unit::TestCase
assert_nothing_raised(TypeError, '[Bug #5020] [ruby-dev:44088]') do
Rational(1,2).coerce(Complex(1,1))
end
+
+ assert_raise(ZeroDivisionError) do
+ 1 / 0r.coerce(0+0i)[0]
+ end
+ assert_raise(ZeroDivisionError) do
+ 1 / 0r.coerce(0.0+0i)[0]
+ end
end
class ObjectX
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index 38147c7446..038c1c830f 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -218,6 +218,17 @@ class TestRegexp < Test::Unit::TestCase
def test_assign_named_capture_to_reserved_word
/(?<nil>.)/ =~ "a"
assert_not_include(local_variables, :nil, "[ruby-dev:32675]")
+
+ def (obj = Object.new).test(s, nil: :ng)
+ /(?<nil>.)/ =~ s
+ binding.local_variable_get(:nil)
+ end
+ assert_equal("b", obj.test("b"))
+
+ tap do |nil: :ng|
+ /(?<nil>.)/ =~ "c"
+ assert_equal("c", binding.local_variable_get(:nil))
+ end
end
def test_assign_named_capture_to_const
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index 4e403a9ffd..ab462bddf4 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -1265,6 +1265,15 @@ eom
assert_nil obj.test
end
+ def test_assignment_return_in_loop
+ obj = Object.new
+ def obj.test
+ x = nil
+ _y = (return until x unless x)
+ end
+ assert_nil obj.test, "[Bug #16695]"
+ end
+
def test_method_call_location
line = __LINE__+5
e = assert_raise(NoMethodError) do
@@ -1306,6 +1315,12 @@ eom
assert_valid_syntax('obj::foo (1) {}')
end
+ def test_value_expr_in_condition
+ mesg = /void value expression/
+ assert_syntax_error("tap {a = (true ? next : break)}", mesg)
+ assert_valid_syntax("tap {a = (true ? true : break)}")
+ end
+
private
def not_label(x) @result = x; @not_label ||= nil end
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index 2bd4bc8455..3b3711b805 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -851,6 +851,13 @@ class TestTime < Test::Unit::TestCase
assert_equal(8192, Time.now.strftime('%8192z').size)
end
+ def test_strftime_wide_precision
+ t2000 = get_t2000
+ s = t2000.strftime("%28c")
+ assert_equal(28, s.size)
+ assert_equal(t2000.strftime("%c"), s.strip)
+ end
+
def test_strfimte_zoneoffset
t2000 = get_t2000
t = t2000.getlocal("+09:00:00")
@@ -1101,6 +1108,14 @@ class TestTime < Test::Unit::TestCase
}
end
+ def test_getlocal_utc_offset
+ t = Time.gm(2000)
+ assert_equal [00, 30, 21, 31, 12, 1999], t.getlocal("-02:30").to_a[0, 6]
+ assert_equal [00, 00, 9, 1, 1, 2000], t.getlocal("+09:00").to_a[0, 6]
+ assert_equal [20, 29, 21, 31, 12, 1999], t.getlocal("-02:30:40").to_a[0, 6]
+ assert_equal [35, 10, 9, 1, 1, 2000], t.getlocal("+09:10:35").to_a[0, 6]
+ end
+
def test_getlocal_nil
now = Time.now
now2 = nil
diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb
index 21ac667281..9bba30e577 100644
--- a/test/ruby/test_time_tz.rb
+++ b/test/ruby/test_time_tz.rb
@@ -548,6 +548,8 @@ module TestTimeTZ::WithTZ
m, s = (4000-utc_offset).divmod(60)
h, m = m.divmod(60)
assert_equal(time_class.utc(2018, 9, 1, 12+h, m, s), t)
+ assert_equal(6, t.wday)
+ assert_equal(244, t.yday)
end
def subtest_at(time_class, tz, tzarg, tzname, abbr, utc_offset)
@@ -657,6 +659,16 @@ else
@tz ||= TZInfo::Timezone.get(tzname)
end
end
+
+ def test_fractional_second
+ x = Object.new
+ def x.local_to_utc(t); t + 8*3600; end
+ def x.utc_to_local(t); t - 8*3600; end
+
+ t1 = Time.new(2020,11,11,12,13,14.124r, '-08:00')
+ t2 = Time.new(2020,11,11,12,13,14.124r, x)
+ assert_equal(t1, t2)
+ end
end
begin
diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb
index 44d238ffd2..7f81cbf424 100644
--- a/test/ruby/test_transcode.rb
+++ b/test/ruby/test_transcode.rb
@@ -2161,6 +2161,14 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback.method(:escape)))
end
+ def test_fallback_aref
+ fallback = Object.new
+ def fallback.[](x)
+ "U+%.4X" % x.unpack("U")
+ end
+ assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback))
+ end
+
bug8940 = '[ruby-core:57318] [Bug #8940]'
%w[UTF-32 UTF-16].each do |enc|
define_method("test_pseudo_encoding_inspect(#{enc})") do
diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb
index 97a64af323..3f543d4acf 100644
--- a/test/rubygems/test_bundled_ca.rb
+++ b/test/rubygems/test_bundled_ca.rb
@@ -51,13 +51,16 @@ if ENV["CI"] || ENV["TEST_SSL"]
assert_https('rubygems.org')
end
- def test_accessing_fastly
- assert_https('rubygems.global.ssl.fastly.net')
+ def test_accessing_www_rubygems
+ assert_https('www.rubygems.org')
end
- def test_accessing_new_index
- assert_https('fastly.rubygems.org')
+ def test_accessing_staging
+ assert_https('staging.rubygems.org')
end
+ def test_accessing_new_index
+ assert_https('index.rubygems.org')
+ end
end
end
diff --git a/test/test_tmpdir.rb b/test/test_tmpdir.rb
index 1e633d233b..bede8c0ab4 100644
--- a/test/test_tmpdir.rb
+++ b/test/test_tmpdir.rb
@@ -65,22 +65,40 @@ class TestTmpdir < Test::Unit::TestCase
}
end
- TRAVERSAL_PATH = Array.new(Dir.pwd.split('/').count, '..').join('/') + Dir.pwd + '/'
- TRAVERSAL_PATH.delete!(':') if /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_mktmpdir_mutate
+ bug16918 = '[ruby-core:98563]'
+ assert_nothing_raised(bug16918) do
+ assert_mktmpdir_traversal do |traversal_path|
+ Dir.mktmpdir(traversal_path + 'foo') do |actual|
+ actual << "foo"
+ end
+ end
+ end
+ end
def test_mktmpdir_traversal
- expect = Dir.glob(TRAVERSAL_PATH + '*').count
- Dir.mktmpdir(TRAVERSAL_PATH + 'foo') do
- actual = Dir.glob(TRAVERSAL_PATH + '*').count
- assert_equal expect, actual
+ assert_mktmpdir_traversal do |traversal_path|
+ Dir.mktmpdir(traversal_path + 'foo') do |actual|
+ actual
+ end
end
end
def test_mktmpdir_traversal_array
- expect = Dir.glob(TRAVERSAL_PATH + '*').count
- Dir.mktmpdir([TRAVERSAL_PATH, 'foo']) do
- actual = Dir.glob(TRAVERSAL_PATH + '*').count
- assert_equal expect, actual
+ assert_mktmpdir_traversal do |traversal_path|
+ Dir.mktmpdir([traversal_path, 'foo']) do |actual|
+ actual
+ end
+ end
+ end
+
+ def assert_mktmpdir_traversal
+ Dir.mktmpdir do |target|
+ target = target.chomp('/') + '/'
+ traversal_path = target.sub(/\A\w:/, '') # for DOSISH
+ traversal_path = Array.new(target.count('/')-2, '..').join('/') + traversal_path
+ actual = yield traversal_path
+ assert_not_send([File.absolute_path(actual), :start_with?, target])
end
end
end
diff --git a/test/uri/test_ldap.rb b/test/uri/test_ldap.rb
index adad4454b5..64845e487a 100644
--- a/test/uri/test_ldap.rb
+++ b/test/uri/test_ldap.rb
@@ -95,6 +95,10 @@ class TestLDAP < Test::Unit::TestCase
u.select(:scheme, :host, :not_exist, :port)
end
end
+
+ def test_parse_invalid_uri
+ assert_raise(URI::InvalidURIError) {URI.parse("ldap:https://example.com")}
+ end
end
diff --git a/test/webrick/test_httpproxy.rb b/test/webrick/test_httpproxy.rb
index a9f6f7d610..504eb1f915 100644
--- a/test/webrick/test_httpproxy.rb
+++ b/test/webrick/test_httpproxy.rb
@@ -213,7 +213,7 @@ class TestWEBrickHTTPProxy < Test::Unit::TestCase
end
end
end
- end
+ end if RUBY_VERSION >= '2.5'
def make_certificate(key, cn)
subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=#{cn}")
diff --git a/test/webrick/test_httpserver.rb b/test/webrick/test_httpserver.rb
index a6e70da7e8..2e5d44940c 100644
--- a/test/webrick/test_httpserver.rb
+++ b/test/webrick/test_httpserver.rb
@@ -253,7 +253,7 @@ class TestWEBrickHTTPServer < Test::Unit::TestCase
server.virtual_host(WEBrick::HTTPServer.new(vhost_config))
Thread.pass while server.status != :Running
- sleep 1 if RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
+ sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
assert_equal(1, started, log.call)
assert_equal(0, stopped, log.call)
assert_equal(0, accepted, log.call)
diff --git a/test/webrick/test_server.rb b/test/webrick/test_server.rb
index 5f7f3a0b58..8162a186db 100644
--- a/test/webrick/test_server.rb
+++ b/test/webrick/test_server.rb
@@ -65,7 +65,7 @@ class TestWEBrickServer < Test::Unit::TestCase
}
TestWEBrick.start_server(Echo, config){|server, addr, port, log|
true while server.status != :Running
- sleep 1 if RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
+ sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
assert_equal(1, started, log.call)
assert_equal(0, stopped, log.call)
assert_equal(0, accepted, log.call)
diff --git a/time.c b/time.c
index 75db5540c9..228100a9d1 100644
--- a/time.c
+++ b/time.c
@@ -4322,14 +4322,15 @@ time_wday(VALUE time)
GetTimeval(time, tobj);
MAKE_TM(time, tobj);
+ if (tobj->vtm.wday == VTM_WDAY_INITVAL) {
+ VALUE zone = tobj->vtm.zone;
+ if (!NIL_P(zone)) zone_localtime(zone, time);
+ }
return INT2FIX((int)tobj->vtm.wday);
}
#define wday_p(n) {\
- struct time_object *tobj;\
- GetTimeval(time, tobj);\
- MAKE_TM(time, tobj);\
- return (tobj->vtm.wday == (n)) ? Qtrue : Qfalse;\
+ return (time_wday(time) == INT2FIX(n)) ? Qtrue : Qfalse; \
}
/*
@@ -4461,6 +4462,10 @@ time_yday(VALUE time)
GetTimeval(time, tobj);
MAKE_TM(time, tobj);
+ if (tobj->vtm.yday == 0) {
+ VALUE zone = tobj->vtm.zone;
+ if (!NIL_P(zone)) zone_localtime(zone, time);
+ }
return INT2FIX(tobj->vtm.yday);
}
@@ -5147,6 +5152,7 @@ tm_from_time(VALUE klass, VALUE time)
ttm = DATA_PTR(tm);
v = &vtm;
GMTIMEW(ttm->timew = tobj->timew, v);
+ ttm->timew = wsub(ttm->timew, v->subsecx);
v->subsecx = INT2FIX(0);
v->zone = Qnil;
ttm->vtm = *v;
diff --git a/tool/m4/ruby_universal_arch.m4 b/tool/m4/ruby_universal_arch.m4
index a524bc2592..dd9ad7ee46 100644
--- a/tool/m4/ruby_universal_arch.m4
+++ b/tool/m4/ruby_universal_arch.m4
@@ -66,6 +66,9 @@ AS_IF([test ${target_archs+set}], [
#ifdef __ppc64__
"processor-name=powerpc64"
#endif
+#ifdef __arm64__
+"processor-name=arm64"
+#endif
EOF
sed -n 's/^"processor-name=\(.*\)"/\1/p'`
target="$target_cpu${target}"
diff --git a/tool/make-snapshot b/tool/make-snapshot
index 89aad4b932..c8f4b0a628 100755
--- a/tool/make-snapshot
+++ b/tool/make-snapshot
@@ -394,24 +394,21 @@ def package(vcs, rev, destdir, tmp = nil)
mk << commonmk.gsub(/\{\$([^(){}]*)[^{}]*\}/, "")
mk << <<-'APPEND'
-prereq: clean-cache $(CLEAN_CACHE)
+prepare-package: touch-unicode-files:
+prepare-package: prereq update-download clean-cache $(CLEAN_CACHE)
clean-cache $(CLEAN_CACHE): after-update
-touch-unicode-files: _touch-unicode-files
-update-download:: touch-unicode-files
update-download:: update-gems
after-update:: extract-gems
extract-gems: update-gems
update-gems:
-_touch-unicode-files:
- $(MAKEDIRS) $(UNICODE_SRC_DATA_DIR)
- touch $(UNICODE_SRC_DATA_DIR)/.unicode-tables.time $(UNICODE_DATA_HEADERS)
+$(UNICODE_SRC_DATA_DIR)/.unicode-tables.time:
+touch-unicode-files:
APPEND
open(clean.add("Makefile"), "w") do |f|
- f.puts "prereq: update-download"
f.puts mk
end
ENV["CACHE_SAVE"] = "no"
- system(ENV["MAKE"] || ENV["make"] || "make", "prereq", *args.map {|arg| arg.join("=")})
+ system(ENV["MAKE"] || ENV["make"] || "make", "prepare-package", *args.map {|arg| arg.join("=")})
clean.push("rbconfig.rb", ".rbconfig.time", "enc.mk")
print "prerequisites"
else
diff --git a/tool/pure_parser.rb b/tool/pure_parser.rb
new file mode 100644
index 0000000000..aa00a2defa
--- /dev/null
+++ b/tool/pure_parser.rb
@@ -0,0 +1,20 @@
+#!/usr/bin/ruby -pi
+BEGIN {
+ require_relative 'colorize'
+
+ colorize = Colorize.new
+ file = ARGV.shift
+ begin
+ version = IO.popen(ARGV+%w[--version], &:read)
+ rescue Errno::ENOENT
+ abort "Failed to run `#{colorize.fail ARGV.join(' ')}'; You may have to install it."
+ end
+ unless /\Abison .* (\d+)\.\d+/ =~ version
+ puts colorize.fail("not bison")
+ exit
+ end
+ exit if $1.to_i >= 3
+ ARGV.clear
+ ARGV.push(file)
+}
+$_.sub!(/^%define\s+api\.pure/, '%pure-parser')
diff --git a/transcode.c b/transcode.c
index 5f9e4e9600..55ff30c934 100644
--- a/transcode.c
+++ b/transcode.c
@@ -12,6 +12,7 @@
#include "ruby/encoding.h"
#include "internal.h"
#include "transcode_data.h"
+#include "id.h"
#include <ctype.h>
#define ENABLE_ECONV_NEWLINE_OPTION 1
@@ -23,7 +24,7 @@ static VALUE rb_eConverterNotFoundError;
VALUE rb_cEncodingConverter;
-static VALUE sym_invalid, sym_undef, sym_replace, sym_fallback, sym_aref;
+static VALUE sym_invalid, sym_undef, sym_replace, sym_fallback;
static VALUE sym_xml, sym_text, sym_attr;
static VALUE sym_universal_newline;
static VALUE sym_crlf_newline;
@@ -2256,7 +2257,7 @@ method_fallback(VALUE fallback, VALUE c)
static VALUE
aref_fallback(VALUE fallback, VALUE c)
{
- return rb_funcall3(fallback, sym_aref, 1, &c);
+ return rb_funcallv_public(fallback, idAREF, 1, &c);
}
static void
@@ -2550,7 +2551,7 @@ rb_econv_prepare_options(VALUE opthash, VALUE *opts, int ecflags)
if (!NIL_P(v)) {
VALUE h = rb_check_hash_type(v);
if (NIL_P(h)
- ? (rb_obj_is_proc(v) || rb_obj_is_method(v) || rb_respond_to(v, sym_aref))
+ ? (rb_obj_is_proc(v) || rb_obj_is_method(v) || rb_respond_to(v, idAREF))
: (v = h, 1)) {
if (NIL_P(newhash))
newhash = rb_hash_new();
@@ -4424,7 +4425,6 @@ Init_transcode(void)
sym_undef = ID2SYM(rb_intern("undef"));
sym_replace = ID2SYM(rb_intern("replace"));
sym_fallback = ID2SYM(rb_intern("fallback"));
- sym_aref = ID2SYM(rb_intern("[]"));
sym_xml = ID2SYM(rb_intern("xml"));
sym_text = ID2SYM(rb_intern("text"));
sym_attr = ID2SYM(rb_intern("attr"));
diff --git a/util.c b/util.c
index 7c83b50b8b..814404fdf1 100644
--- a/util.c
+++ b/util.c
@@ -2081,6 +2081,7 @@ break2:
if (!*++s || !(s1 = strchr(hexdigit, *s))) goto ret0;
if (*s == '0') {
while (*++s == '0');
+ if (!*s) goto ret;
s1 = strchr(hexdigit, *s);
}
if (s1 != NULL) {
@@ -2103,7 +2104,7 @@ break2:
for (; *s && (s1 = strchr(hexdigit, *s)); ++s) {
adj += aadj * ((s1 - hexdigit) & 15);
if ((aadj /= 16) == 0.0) {
- while (strchr(hexdigit, *++s));
+ while (*++s && strchr(hexdigit, *s));
break;
}
}
diff --git a/variable.c b/variable.c
index 89befc0c89..c3100f6e19 100644
--- a/variable.c
+++ b/variable.c
@@ -1984,9 +1984,11 @@ struct autoload_const {
VALUE mod;
VALUE ad; /* autoload_data_i */
VALUE value;
+ VALUE file;
ID id;
int safe_level;
rb_const_flag_t flag;
+ int line;
};
/* always on stack, no need to mark */
@@ -2048,6 +2050,7 @@ autoload_c_mark(void *ptr)
rb_gc_mark(ac->mod);
rb_gc_mark(ac->ad);
rb_gc_mark(ac->value);
+ rb_gc_mark(ac->file);
}
static void
@@ -2838,6 +2841,7 @@ rb_const_set(VALUE klass, ID id, VALUE val)
}
else {
struct autoload_const ac;
+ memset(&ac, 0, sizeof(ac));
ac.mod = klass;
ac.id = id;
ac.value = val;
@@ -2908,10 +2912,17 @@ const_tbl_update(struct autoload_const *ac)
rb_clear_constant_cache();
ac->value = val; /* autoload_i is non-WB-protected */
- return;
+ ac->file = rb_source_location(&ac->line);
}
- /* otherwise, allow to override */
- autoload_delete(klass, id);
+ else {
+ /* otherwise autoloaded constant, allow to override */
+ autoload_delete(klass, id);
+ ce->flag = visibility;
+ RB_OBJ_WRITE(klass, &ce->value, val);
+ RB_OBJ_WRITE(klass, &ce->file, ac->file);
+ ce->line = ac->line;
+ }
+ return;
}
else {
VALUE name = QUOTE_ID(id);
diff --git a/version.h b/version.h
index cc4918ba10..e60fbb787f 100644
--- a/version.h
+++ b/version.h
@@ -1,10 +1,10 @@
-#define RUBY_VERSION "2.6.7"
-#define RUBY_RELEASE_DATE "2020-06-14"
-#define RUBY_PATCHLEVEL 148
+#define RUBY_VERSION "2.6.10"
+#define RUBY_RELEASE_DATE "2022-04-12"
+#define RUBY_PATCHLEVEL 210
-#define RUBY_RELEASE_YEAR 2020
-#define RUBY_RELEASE_MONTH 6
-#define RUBY_RELEASE_DAY 14
+#define RUBY_RELEASE_YEAR 2022
+#define RUBY_RELEASE_MONTH 4
+#define RUBY_RELEASE_DAY 12
#include "ruby/version.h"
diff --git a/vm.c b/vm.c
index 6d54a81719..04b8b96f53 100644
--- a/vm.c
+++ b/vm.c
@@ -2263,6 +2263,8 @@ free_loading_table_entry(st_data_t key, st_data_t value, st_data_t arg)
return ST_DELETE;
}
+extern void rb_native_mutex_destroy(rb_nativethread_lock_t *lock);
+
int
ruby_vm_destruct(rb_vm_t *vm)
{
@@ -2292,6 +2294,8 @@ ruby_vm_destruct(rb_vm_t *vm)
if (objspace) {
rb_objspace_free(objspace);
}
+ rb_native_mutex_destroy(&vm->waitpid_lock);
+ rb_native_mutex_destroy(&vm->workqueue_lock);
/* after freeing objspace, you *can't* use ruby_xfree() */
ruby_mimfree(vm);
ruby_current_vm_ptr = NULL;
@@ -2484,7 +2488,7 @@ rb_execution_context_mark(const rb_execution_context_t *ec)
rb_gc_mark_machine_stack(ec);
rb_gc_mark_locations((VALUE *)&ec->machine.regs,
(VALUE *)(&ec->machine.regs) +
- sizeof(ec->machine.regs) / sizeof(VALUE));
+ sizeof(ec->machine.regs) / (sizeof(VALUE)));
}
RUBY_MARK_UNLESS_NULL(ec->errinfo);
diff --git a/vm_args.c b/vm_args.c
index 299ed16aa8..2e70ed3fcf 100644
--- a/vm_args.c
+++ b/vm_args.c
@@ -685,17 +685,6 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
else if (!NIL_P(keyword_hash) && RHASH_SIZE(keyword_hash) > 0) {
argument_kw_error(ec, iseq, "unknown", rb_hash_keys(keyword_hash));
}
- else if (kw_splat && NIL_P(keyword_hash)) {
- if (RTEST(ruby_verbose)) {
- VALUE path = rb_iseq_path(iseq);
- VALUE line = rb_iseq_first_lineno(iseq);
- VALUE label = rb_iseq_label(iseq);
- rb_compile_warning(NIL_P(path) ? NULL : RSTRING_PTR(path), FIX2INT(line),
- "in `%s': the last argument was passed as a single Hash",
- NIL_P(label) ? NULL : RSTRING_PTR(label));
- rb_warning("although a splat keyword arguments here");
- }
- }
if (iseq->body->param.flags.has_block) {
if (iseq->body->local_iseq == iseq) {
diff --git a/vm_backtrace.c b/vm_backtrace.c
index 8264018da0..2a6c5bde30 100644
--- a/vm_backtrace.c
+++ b/vm_backtrace.c
@@ -1402,7 +1402,7 @@ rb_profile_frame_classpath(VALUE frame)
}
else if (FL_TEST(klass, FL_SINGLETON)) {
klass = rb_ivar_get(klass, id__attached__);
- if (!RB_TYPE_P(klass, T_CLASS))
+ if (!RB_TYPE_P(klass, T_CLASS) && !RB_TYPE_P(klass, T_MODULE))
return rb_sprintf("#<%s:%p>", rb_class2name(rb_obj_class(klass)), (void*)klass);
}
return rb_class_path(klass);
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 4a47a8ee5c..0a018bdd54 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -2161,8 +2161,8 @@ current_method_entry(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
return cfp;
}
-static VALUE
-find_defined_class_by_owner(VALUE current_class, VALUE target_owner)
+MJIT_FUNC_EXPORTED VALUE
+rb_find_defined_class_by_owner(VALUE current_class, VALUE target_owner)
{
VALUE klass = current_class;
@@ -2187,7 +2187,7 @@ aliased_callable_method_entry(const rb_callable_method_entry_t *me)
const rb_callable_method_entry_t *cme;
if (orig_me->defined_class == 0) {
- VALUE defined_class = find_defined_class_by_owner(me->defined_class, orig_me->owner);
+ VALUE defined_class = rb_find_defined_class_by_owner(me->defined_class, orig_me->owner);
VM_ASSERT(RB_TYPE_P(orig_me->owner, T_MODULE));
cme = rb_method_entry_complement_defined_class(orig_me, me->called_id, defined_class);
@@ -2858,7 +2858,7 @@ vm_defined(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t op_
case METHOD_VISI_PRIVATE:
break;
case METHOD_VISI_PROTECTED:
- if (!rb_obj_is_kind_of(GET_SELF(), rb_class_real(klass))) {
+ if (!rb_obj_is_kind_of(GET_SELF(), rb_class_real(me->defined_class))) {
break;
}
case METHOD_VISI_PUBLIC: