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/