[前][次][番号順一覧][スレッド一覧]

ruby-changes:58390

From: Jeremy <ko1@a...>
Date: Thu, 24 Oct 2019 18:39:35 +0900 (JST)
Subject: [ruby-changes:58390] 4e40ca301c (master): [ruby/date] Check for numeric arguments in constructors

https://git.ruby-lang.org/ruby.git/commit/?id=4e40ca301c

From 4e40ca301cca692361627ac6db06c0f0074636f0 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Wed, 24 Jul 2019 08:03:27 -0700
Subject: [ruby/date] Check for numeric arguments in constructors

Previously, the type of these arguments were not checked, leading to
NoMethodErrors in some cases, and TypeErrors in other cases, but not
showing what field was having the problems.  This change makes it so
the field with the problem is included in the error message.

For the valid_*? methods, this changes them to return false if one
of the arguments that should be numeric is not.

Fixes Ruby Bug 11935
Fixes Ruby Misc 15298

https://github.com/ruby/date/commit/a2f4b665f8

diff --git a/ext/date/date_core.c b/ext/date/date_core.c
index 71d0da5..0483d2d 100644
--- a/ext/date/date_core.c
+++ b/ext/date/date_core.c
@@ -54,6 +54,14 @@ static double positive_inf, negative_inf; https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L54
 static VALUE date_initialize(int argc, VALUE *argv, VALUE self);
 static VALUE datetime_initialize(int argc, VALUE *argv, VALUE self);
 
+#define RETURN_FALSE_UNLESS_NUMERIC(obj) if(!RTEST(rb_obj_is_kind_of((obj), rb_cNumeric))) return Qfalse
+inline static void
+check_numeric(VALUE obj, const char* field) {
+    if(!RTEST(rb_obj_is_kind_of(obj, rb_cNumeric))) {
+        rb_raise(rb_eTypeError, "invalid %s (not numeric)", field);
+    }
+}
+
 inline static int
 f_cmp(VALUE x, VALUE y)
 {
@@ -2470,6 +2478,7 @@ date_s_valid_jd_p(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L2478
 
     rb_scan_args(argc, argv, "11", &vjd, &vsg);
 
+    RETURN_FALSE_UNLESS_NUMERIC(vjd);
     argv2[0] = vjd;
     if (argc < 2)
 	argv2[1] = INT2FIX(DEFAULT_SG);
@@ -2562,6 +2571,9 @@ date_s_valid_civil_p(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L2571
 
     rb_scan_args(argc, argv, "31", &vy, &vm, &vd, &vsg);
 
+    RETURN_FALSE_UNLESS_NUMERIC(vy);
+    RETURN_FALSE_UNLESS_NUMERIC(vm);
+    RETURN_FALSE_UNLESS_NUMERIC(vd);
     argv2[0] = vy;
     argv2[1] = vm;
     argv2[2] = vd;
@@ -2643,6 +2655,8 @@ date_s_valid_ordinal_p(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L2655
 
     rb_scan_args(argc, argv, "21", &vy, &vd, &vsg);
 
+    RETURN_FALSE_UNLESS_NUMERIC(vy);
+    RETURN_FALSE_UNLESS_NUMERIC(vd);
     argv2[0] = vy;
     argv2[1] = vd;
     if (argc < 3)
@@ -2725,6 +2739,9 @@ date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L2739
 
     rb_scan_args(argc, argv, "31", &vy, &vw, &vd, &vsg);
 
+    RETURN_FALSE_UNLESS_NUMERIC(vy);
+    RETURN_FALSE_UNLESS_NUMERIC(vw);
+    RETURN_FALSE_UNLESS_NUMERIC(vd);
     argv2[0] = vy;
     argv2[1] = vw;
     argv2[2] = vd;
@@ -2906,6 +2923,7 @@ date_s_julian_leap_p(VALUE klass, VALUE y) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L2923
     VALUE nth;
     int ry;
 
+    check_numeric(y, "year");
     decode_year(y, +1, &nth, &ry);
     return f_boolcast(c_julian_leap_p(ry));
 }
@@ -2927,6 +2945,7 @@ date_s_gregorian_leap_p(VALUE klass, VALUE y) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L2945
     VALUE nth;
     int ry;
 
+    check_numeric(y, "year");
     decode_year(y, -1, &nth, &ry);
     return f_boolcast(c_gregorian_leap_p(ry));
 }
@@ -3281,6 +3300,7 @@ date_s_jd(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L3300
       case 2:
 	val2sg(vsg, sg);
       case 1:
+        check_numeric(vjd, "jd");
 	num2num_with_frac(jd, positive_inf);
     }
 
@@ -3333,8 +3353,10 @@ date_s_ordinal(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L3353
       case 3:
 	val2sg(vsg, sg);
       case 2:
+        check_numeric(vd, "yday");
 	num2int_with_frac(d, positive_inf);
       case 1:
+        check_numeric(vy, "year");
 	y = vy;
     }
 
@@ -3413,10 +3435,13 @@ date_initialize(int argc, VALUE *argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L3435
       case 4:
 	val2sg(vsg, sg);
       case 3:
+        check_numeric(vd, "day");
 	num2int_with_frac(d, positive_inf);
       case 2:
+        check_numeric(vd, "month");
 	m = NUM2INT(vm);
       case 1:
+        check_numeric(vy, "year");
 	y = vy;
     }
 
@@ -3483,10 +3508,13 @@ date_s_commercial(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L3508
       case 4:
 	val2sg(vsg, sg);
       case 3:
+        check_numeric(vd, "cwday");
 	num2int_with_frac(d, positive_inf);
       case 2:
+        check_numeric(vw, "cweek");
 	w = NUM2INT(vw);
       case 1:
+        check_numeric(vy, "year");
 	y = vy;
     }
 
@@ -7244,12 +7272,16 @@ datetime_s_jd(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L7272
       case 5:
 	val2off(vof, rof);
       case 4:
+        check_numeric(vs, "second");
 	num2int_with_frac(s, positive_inf);
       case 3:
+        check_numeric(vmin, "minute");
 	num2int_with_frac(min, 3);
       case 2:
+        check_numeric(vh, "hour");
 	num2int_with_frac(h, 2);
       case 1:
+        check_numeric(vjd, "jd");
 	num2num_with_frac(jd, 1);
     }
 
@@ -7313,14 +7345,19 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L7345
       case 6:
 	val2off(vof, rof);
       case 5:
+        check_numeric(vs, "second");
 	num2int_with_frac(s, positive_inf);
       case 4:
+        check_numeric(vmin, "minute");
 	num2int_with_frac(min, 4);
       case 3:
+        check_numeric(vh, "hour");
 	num2int_with_frac(h, 3);
       case 2:
+        check_numeric(vd, "yday");
 	num2int_with_frac(d, 2);
       case 1:
+        check_numeric(vy, "year");
 	y = vy;
     }
 
@@ -7401,16 +7438,22 @@ datetime_initialize(int argc, VALUE *argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L7438
       case 7:
 	val2off(vof, rof);
       case 6:
+        check_numeric(vs, "second");
 	num2int_with_frac(s, positive_inf);
       case 5:
+        check_numeric(vmin, "minute");
 	num2int_with_frac(min, 5);
       case 4:
+        check_numeric(vh, "hour");
 	num2int_with_frac(h, 4);
       case 3:
+        check_numeric(vd, "day");
 	num2int_with_frac(d, 3);
       case 2:
+        check_numeric(vm, "month");
 	m = NUM2INT(vm);
       case 1:
+        check_numeric(vy, "year");
 	y = vy;
     }
 
@@ -7499,16 +7542,22 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/ext/date/date_core.c#L7542
       case 7:
 	val2off(vof, rof);
       case 6:
+        check_numeric(vs, "second");
 	num2int_with_frac(s, positive_inf);
       case 5:
+        check_numeric(vmin, "minute");
 	num2int_with_frac(min, 5);
       case 4:
+        check_numeric(vh, "hour");
 	num2int_with_frac(h, 4);
       case 3:
+        check_numeric(vd, "cwday");
 	num2int_with_frac(d, 3);
       case 2:
+        check_numeric(vw, "cweek");
 	w = NUM2INT(vw);
       case 1:
+        check_numeric(vy, "year");
 	y = vy;
     }
 
diff --git a/test/date/test_date_new.rb b/test/date/test_date_new.rb
index d27116b..2fb9846 100644
--- a/test/date/test_date_new.rb
+++ b/test/date/test_date_new.rb
@@ -32,6 +32,56 @@ class TestDateNew < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/date/test_date_new.rb#L32
     end
   end
 
+  def test_valid_with_invalid_types
+    o = Object.new
+    assert_equal(false, Date.valid_jd?(o))
+    assert_equal(false, Date.valid_civil?(o, 1, 1))
+    assert_equal(false, Date.valid_civil?(1, o, 1))
+    assert_equal(false, Date.valid_civil?(1, 1, o))
+    assert_equal(false, Date.valid_ordinal?(o, 1))
+    assert_equal(false, Date.valid_ordinal?(1, o))
+    assert_equal(false, Date.valid_commercial?(o, 1, 1))
+    assert_equal(false, Date.valid_commercial?(1, o, 1))
+    assert_equal(false, Date.valid_commercial?(1, 1, o))
+  end
+
+  def test_invalid_types
+    o = Object.new
+    assert_raise(TypeError) { Date.julian_leap?(o) }
+    assert_raise(TypeError) { Date.gregorian_leap?(o) }
+    assert_raise(TypeError) { Date.jd(o) }
+    assert_raise(TypeError) { Date.new(o) }
+    assert_raise(TypeError) { Date.new(1, o) }
+    assert_raise(TypeError) { Date.new(1, 1, o) }
+    assert_raise(TypeError) { Date.ordinal(o) }
+    assert_raise(TypeError) { Date.ordinal(1, o) }
+    assert_raise(TypeError) { Date.commercial(o) }
+    assert_raise(TypeError) { Date.commercial(1, o) }
+    assert_raise(TypeError) { Date.commercial(1, 1, o) }
+
+    assert_raise(TypeError) { DateTime.jd(o) }
+    assert_raise(TypeError) { DateTime.jd(1, o) }
+    assert_raise(TypeError) { DateTime.jd(1, 1, o) }
+    assert_raise(TypeError) { DateTime.jd(1, 1, 1, o) }
+    assert_raise(TypeError) { DateTime.new(o) }
+    assert_raise(TypeError) { DateTime.new(1, o) }
+    assert_raise(TypeError) { DateTime.new(1, 1, o) }
+    assert_raise(TypeError) { DateTime.new(1, 1, 1, o) }
+    assert_raise(TypeError) { DateTime.new(1, 1, 1, 1, o) }
+    assert_raise(TypeError) { DateTime.new(1, 1, 1, 1, 1, o) }
+    assert_raise(TypeError) { DateTime.ordinal(o) }
+    assert_raise(TypeError) { DateTime.ordinal(1, o) }
+    assert_raise(TypeError) { DateTime.ordinal(1, 1, o) }
+    assert_raise(TypeError) { DateTime.ordinal(1, 1, 1, o) }
+    assert_raise(TypeError) { DateTime.ordinal(1, 1, 1, 1, o) }
+    assert_raise(TypeError) { DateTime.commercial(o) }
+    assert_raise(TypeError) { DateTime.commercial(1, o) }
+    assert_raise(TypeError) { DateTime.commercial(1, 1, o) }
+    assert_raise(TypeError) { DateTime.commercial(1, 1, 1, o) }
+    assert_raise(TypeError) { DateTime.commercial(1, 1, 1, 1, o) }
+    assert_raise(TypeError) { DateTime.commercial(1, 1, 1, 1, 1, o) }
+  end
+
   def test_ordinal
     d = D (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]