From 39eadca76b48fc7841da688f6745e40897ec37ff Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Sat, 6 Apr 2019 00:02:11 -0700 Subject: Add FrozenError#receiver Similar to NameError#receiver, this returns the object on which the modification was attempted. This is useful as it can pinpoint exactly what is frozen. In many cases when a FrozenError is raised, you cannot determine from the context which object is frozen that you attempted to modify. Users of the current rb_error_frozen C function will have to switch to using rb_error_frozen_object or the new rb_frozen_error_raise in order to set the receiver of the FrozenError. To allow the receiver to be set from Ruby, support an optional second argument to FrozenError#initialize. Implements [Feature #15751] --- error.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) (limited to 'error.c') diff --git a/error.c b/error.c index 403516a683..039999cf6a 100644 --- a/error.c +++ b/error.c @@ -1396,6 +1396,32 @@ exit_success_p(VALUE exc) return Qfalse; } +/* + * call-seq: + * FrozenError.new(msg=nil, receiver=nil) -> name_error + * + * Construct a new FrozenError exception. If given the receiver + * parameter may subsequently be examined using the FrozenError#receiver + * method. + * + * a = [].freeze + * raise FrozenError.new("can't modify frozen array", a) + */ + +static VALUE +frozen_err_initialize(int argc, VALUE *argv, VALUE self) +{ + VALUE mesg, recv; + + argc = rb_scan_args(argc, argv, "02", &mesg, &recv); + if (argc > 1) { + argc--; + rb_ivar_set(self, id_recv, recv); + } + rb_call_super(argc, argv); + return self; +} + void rb_name_error(ID id, const char *fmt, ...) { @@ -2483,6 +2509,8 @@ Init_Exception(void) rb_eRuntimeError = rb_define_class("RuntimeError", rb_eStandardError); rb_eFrozenError = rb_define_class("FrozenError", rb_eRuntimeError); + rb_define_method(rb_eFrozenError, "initialize", frozen_err_initialize, -1); + rb_define_method(rb_eFrozenError, "receiver", name_err_receiver, 0); rb_eSecurityError = rb_define_class("SecurityError", rb_eException); rb_eNoMemError = rb_define_class("NoMemoryError", rb_eException); rb_eEncodingError = rb_define_class("EncodingError", rb_eStandardError); @@ -2845,6 +2873,20 @@ rb_error_frozen(const char *what) rb_raise(rb_eFrozenError, "can't modify frozen %s", what); } +void +rb_frozen_error_raise(VALUE frozen_obj, const char *fmt, ...) +{ + va_list args; + VALUE exc, mesg; + + va_start(args, fmt); + mesg = rb_vsprintf(fmt, args); + va_end(args); + exc = rb_exc_new3(rb_eFrozenError, mesg); + rb_ivar_set(exc, id_recv, frozen_obj); + rb_exc_raise(exc); +} + void rb_error_frozen_object(VALUE frozen_obj) { @@ -2855,12 +2897,13 @@ rb_error_frozen_object(VALUE frozen_obj) VALUE path = rb_ary_entry(debug_info, 0); VALUE line = rb_ary_entry(debug_info, 1); - rb_raise(rb_eFrozenError, "can't modify frozen %"PRIsVALUE", created at %"PRIsVALUE":%"PRIsVALUE, - CLASS_OF(frozen_obj), path, line); + rb_frozen_error_raise(frozen_obj, + "can't modify frozen %"PRIsVALUE", created at %"PRIsVALUE":%"PRIsVALUE, + CLASS_OF(frozen_obj), path, line); } else { - rb_raise(rb_eFrozenError, "can't modify frozen %"PRIsVALUE, - CLASS_OF(frozen_obj)); + rb_frozen_error_raise(frozen_obj, "can't modify frozen %"PRIsVALUE, + CLASS_OF(frozen_obj)); } } -- cgit v1.2.3