From 7e0ae8168917278fab2dc040f74aff220d6b8357 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Mon, 23 Sep 2019 16:03:15 -0700 Subject: [PATCH] Support obj.clone(freeze: true) for freezing clone This freezes the clone even if the receiver is not frozen. It is only for consistency with freeze: false not freezing the clone even if the receiver is frozen. Implements [Feature #16175] --- object.c | 41 +++++++++++++++++++++++----------------- test/ruby/test_object.rb | 8 ++++++++ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/object.c b/object.c index 9c603a8f10..fc43604526 100644 --- a/object.c +++ b/object.c @@ -351,9 +351,9 @@ init_copy(VALUE dest, VALUE obj) } } -static int freeze_opt(int argc, VALUE *argv); -static VALUE immutable_obj_clone(VALUE obj, int kwfreeze); -static VALUE mutable_obj_clone(VALUE obj, int kwfreeze); +static VALUE freeze_opt(int argc, VALUE *argv); +static VALUE immutable_obj_clone(VALUE obj, VALUE kwfreeze); +static VALUE mutable_obj_clone(VALUE obj, VALUE kwfreeze); PUREFUNC(static inline int special_object_p(VALUE obj)); /*!< \private */ static inline int special_object_p(VALUE obj) @@ -374,13 +374,13 @@ special_object_p(VALUE obj) /* * call-seq: - * obj.clone(freeze: true) -> an_object + * obj.clone(freeze: frozen?) -> an_object * * Produces a shallow copy of obj---the instance variables of * obj are copied, but not the objects they reference. - * #clone copies the frozen (unless +:freeze+ keyword argument is - * given with a false value) and tainted state of obj. See - * also the discussion under Object#dup. + * By default, #clone copies the frozen state of the object, but the + * +:freeze+ keyword can override this. See also the discussion under + * Object#dup. * * class Klass * attr_accessor :str @@ -400,7 +400,7 @@ special_object_p(VALUE obj) static VALUE rb_obj_clone2(int argc, VALUE *argv, VALUE obj) { - int kwfreeze = freeze_opt(argc, argv); + VALUE kwfreeze = freeze_opt(argc, argv); if (!special_object_p(obj)) return mutable_obj_clone(obj, kwfreeze); return immutable_obj_clone(obj, kwfreeze); @@ -410,16 +410,16 @@ rb_obj_clone2(int argc, VALUE *argv, VALUE obj) VALUE rb_immutable_obj_clone(int argc, VALUE *argv, VALUE obj) { - int kwfreeze = freeze_opt(argc, argv); + VALUE kwfreeze = freeze_opt(argc, argv); return immutable_obj_clone(obj, kwfreeze); } -static int +static VALUE freeze_opt(int argc, VALUE *argv) { static ID keyword_ids[1]; VALUE opt; - VALUE kwfreeze; + VALUE kwfreeze = Qundef; if (!keyword_ids[0]) { CONST_ID(keyword_ids[0], "freeze"); @@ -427,26 +427,26 @@ freeze_opt(int argc, VALUE *argv) rb_scan_args(argc, argv, "0:", &opt); if (!NIL_P(opt)) { rb_get_kwargs(opt, keyword_ids, 0, 1, &kwfreeze); - if (kwfreeze == Qfalse) return FALSE; + if (kwfreeze == Qfalse) return kwfreeze; if (kwfreeze != Qundef && kwfreeze != Qtrue) { rb_raise(rb_eArgError, "unexpected value for freeze: %"PRIsVALUE, rb_obj_class(kwfreeze)); } } - return TRUE; + return kwfreeze; } static VALUE -immutable_obj_clone(VALUE obj, int kwfreeze) +immutable_obj_clone(VALUE obj, VALUE kwfreeze) { - if (!kwfreeze) + if (kwfreeze == Qfalse) rb_raise(rb_eArgError, "can't unfreeze %"PRIsVALUE, rb_obj_class(obj)); return obj; } static VALUE -mutable_obj_clone(VALUE obj, int kwfreeze) +mutable_obj_clone(VALUE obj, VALUE kwfreeze) { VALUE clone, singleton; @@ -461,8 +461,15 @@ mutable_obj_clone(VALUE obj, int kwfreeze) init_copy(clone, obj); rb_funcall(clone, id_init_clone, 1, obj); - if (kwfreeze) { + switch (kwfreeze) { + case Qtrue: + RBASIC(clone)->flags |= FL_FREEZE; + break; + case Qundef: RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE; + break; + default: + break; } return clone; diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index fcb6c2826f..d14c9bc4ba 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -47,6 +47,14 @@ def test_clone a = Object.new def a.b; 2 end + c = a.clone + assert_equal(false, c.frozen?) + assert_equal(2, c.b) + + c = a.clone(freeze: true) + assert_equal(true, c.frozen?) + assert_equal(2, c.b) + a.freeze c = a.clone assert_equal(true, c.frozen?) -- 2.22.0