From 64955d3ffec8b17c350ddbf64aab835777cabb8d Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@ruby-lang.org>
Date: Fri, 25 Jan 2013 10:32:38 +0900
Subject: [PATCH] error.c: defer error message

* error.c (syserr_initialize): defer generating error message until to_s.

* error.c (syserr_to_s): generate error message here.

* inits.c (rb_call_inits): initialize marshal_compat table before
  Exception.

* marshal.c (Init_marshal_compat): split from Init_marshal().

* variable.c (rb_ivar_remove): new utility function to remove an
  instance variable and return its value if defined or Qundef, without
  exceptions.
---
 error.c    | 84 +++++++++++++++++++++++++++++++++++++++++++++++---------------
 inits.c    |  1 +
 internal.h |  1 +
 marshal.c  |  4 +++
 variable.c | 20 +++++++++++----
 5 files changed, 85 insertions(+), 25 deletions(-)

diff --git a/error.c b/error.c
index da24505..ea568fd 100644
--- a/error.c
+++ b/error.c
@@ -1206,6 +1206,13 @@ get_syserr(int n)
     return error;
 }
 
+static VALUE
+syserr_alloc(VALUE klass)
+{
+    NEWOBJ_OF(obj, struct RObject, klass, T_OBJECT);
+    return (VALUE)obj;
+}
+
 /*
  * call-seq:
  *   SystemCallError.new(msg, errno)  -> system_call_error_subclass
@@ -1223,7 +1230,6 @@ syserr_initialize(int argc, VALUE *argv, VALUE self)
 #if !defined(_WIN32)
     char *strerror();
 #endif
-    const char *err;
     VALUE mesg, error;
     VALUE klass = rb_obj_class(self);
 
@@ -1246,25 +1252,8 @@ syserr_initialize(int argc, VALUE *argv, VALUE self)
 	rb_scan_args(argc, argv, "01", &mesg);
 	error = rb_const_get(klass, rb_intern("Errno"));
     }
-    if (!NIL_P(error)) err = strerror(NUM2INT(error));
-    else err = "unknown error";
-    if (!NIL_P(mesg)) {
-	rb_encoding *le = rb_locale_encoding();
-	VALUE str = StringValue(mesg);
-	rb_encoding *me = rb_enc_get(mesg);
-
-	mesg = rb_sprintf("%s - %"PRIsVALUE, err, mesg);
-	if (le != me && rb_enc_asciicompat(me)) {
-	    le = me;
-	}/* else assume err is non ASCII string. */
-	OBJ_INFECT(mesg, str);
-	rb_enc_associate(mesg, le);
-    }
-    else {
-	mesg = rb_str_new2(err);
-	rb_enc_associate(mesg, rb_locale_encoding());
-    }
-    rb_call_super(1, &mesg);
+    rb_call_super(0, 0);
+    rb_iv_set(self, "_mesg", rb_str_new_frozen(mesg));
     rb_iv_set(self, "errno", error);
     return self;
 }
@@ -1284,6 +1273,58 @@ syserr_errno(VALUE self)
 
 /*
  * call-seq:
+ *   system_call_error.to_s   -> string
+ *
+ * Return this SystemCallError's message.
+ */
+
+static VALUE
+syserr_to_s(VALUE self)
+{
+    VALUE mesg = rb_attr_get(self, rb_intern("mesg"));
+    if (NIL_P(mesg)) {
+	const char *err;
+	VALUE error = rb_attr_get(self, rb_intern("errno"));
+	mesg = rb_ivar_remove(self, rb_intern("_mesg"));
+	if (!NIL_P(error)) err = strerror(NUM2INT(error));
+	else err = "unknown error";
+	if (!NIL_P(mesg)) {
+	    rb_encoding *le = rb_locale_encoding();
+	    VALUE str = StringValue(mesg);
+	    rb_encoding *me = rb_enc_get(mesg);
+
+	    mesg = rb_sprintf("%s - %"PRIsVALUE, err, mesg);
+	    if (le != me && rb_enc_asciicompat(me)) {
+		le = me;
+	    }/* else assume err is non ASCII string. */
+	    OBJ_INFECT(mesg, str);
+	    rb_enc_associate(mesg, le);
+	}
+	else {
+	    mesg = rb_str_new2(err);
+	    rb_enc_associate(mesg, rb_locale_encoding());
+	}
+	rb_ivar_set(self, rb_intern("mesg"), mesg);
+    }
+    return mesg;
+}
+
+static VALUE
+syserr_dump(VALUE self)
+{
+    if (FL_TEST(self, FL_FREEZE)) self = rb_obj_dup(self);
+    syserr_to_s(self);
+    return self;
+}
+
+static VALUE
+syserr_load(VALUE real_obj, VALUE load_data)
+{
+    return real_obj;
+}
+
+/*
+ * call-seq:
  *   system_call_error === other  -> true or false
  *
  * Return +true+ if the receiver is a generic +SystemCallError+, or
@@ -1753,9 +1794,12 @@ Init_Exception(void)
 
     syserr_tbl = st_init_numtable();
     rb_eSystemCallError = rb_define_class("SystemCallError", rb_eStandardError);
+    rb_define_alloc_func(rb_eSystemCallError, syserr_alloc);
     rb_define_method(rb_eSystemCallError, "initialize", syserr_initialize, -1);
     rb_define_method(rb_eSystemCallError, "errno", syserr_errno, 0);
+    rb_define_method(rb_eSystemCallError, "to_s", syserr_to_s, 0);
     rb_define_singleton_method(rb_eSystemCallError, "===", syserr_eqq, 1);
+    rb_marshal_define_compat(rb_eSystemCallError, rb_eSystemCallError, syserr_dump, syserr_load);
 
     rb_mErrno = rb_define_module("Errno");
 
diff --git a/inits.c b/inits.c
index fe0aade..bf7c9f9 100644
--- a/inits.c
+++ b/inits.c
@@ -26,6 +26,7 @@ rb_call_inits(void)
     CALL(Comparable);
     CALL(Enumerable);
     CALL(String);
+    CALL(marshal_compat);
     CALL(Exception);
     CALL(eval);
     CALL(safe);
diff --git a/internal.h b/internal.h
index c8317a5..9623f7e 100644
--- a/internal.h
+++ b/internal.h
@@ -363,6 +363,7 @@ void rb_execarg_setenv(VALUE execarg_obj, VALUE env);
 void rb_gc_mark_global_tbl(void);
 void rb_mark_generic_ivar(VALUE);
 void rb_mark_generic_ivar_tbl(void);
+VALUE rb_ivar_remove(VALUE obj, ID id);
 
 #if defined __GNUC__ && __GNUC__ >= 4
 #pragma GCC visibility pop
diff --git a/marshal.c b/marshal.c
index 786a28b..b9d9ad9 100644
--- a/marshal.c
+++ b/marshal.c
@@ -2067,7 +2067,11 @@ Init_marshal(void)
 
     rb_define_const(rb_mMarshal, "MAJOR_VERSION", INT2FIX(MARSHAL_MAJOR));
     rb_define_const(rb_mMarshal, "MINOR_VERSION", INT2FIX(MARSHAL_MINOR));
+}
 
+void
+Init_marshal_compat(void)
+{
     compat_allocator_tbl = st_init_numtable();
     compat_allocator_tbl_wrapper =
 	Data_Wrap_Struct(rb_cData, mark_marshal_compat_t, 0, compat_allocator_tbl);
diff --git a/variable.c b/variable.c
index f6bd70a..6a176c6 100644
--- a/variable.c
+++ b/variable.c
@@ -1396,9 +1396,6 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name)
 {
     VALUE val = Qnil;
     const ID id = rb_check_id(&name);
-    st_data_t n, v;
-    struct st_table *iv_index_tbl;
-    st_data_t index;
 
     if (!OBJ_UNTRUSTED(obj) && rb_safe_level() >= 4)
 	rb_raise(rb_eSecurityError, "Insecure: can't modify instance variable");
@@ -1418,6 +1415,20 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name)
 		      QUOTE_ID(id));
     }
 
+    val = rb_ivar_remove(obj, id);
+    if (val == Qundef)
+	rb_name_error(id, "instance variable %"PRIsVALUE" not defined", QUOTE_ID(id));
+    return val;
+}
+
+VALUE
+rb_ivar_remove(VALUE obj, ID id)
+{
+    VALUE val = Qnil;
+    st_data_t n, v;
+    struct st_table *iv_index_tbl;
+    st_data_t index;
+
     if (SPECIAL_CONST_P(obj)) goto generic;
     switch (BUILTIN_TYPE(obj)) {
       case T_OBJECT:
@@ -1448,9 +1459,8 @@ rb_obj_remove_instance_variable(VALUE obj, VALUE name)
 	}
 	break;
     }
-    rb_name_error(id, "instance variable %"PRIsVALUE" not defined", QUOTE_ID(id));
 
-    UNREACHABLE;
+    return Qundef;
 }
 
 NORETURN(static void uninitialized_constant(VALUE, ID));
-- 
1.8.1.1

