diff options
author | Jeremy Evans <code@jeremyevans.net> | 2019-08-25 21:11:46 -0700 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2020-01-03 20:13:09 -0800 |
commit | 04eb7c7e462d1fcbab9efc7022c1b43636ab878a (patch) | |
tree | 025cb1f1e10736e9c37eb991a8c4cdb1931d4ff2 /object.c | |
parent | 0eeed5bcc5530edb0af2af2ccff09d067c59e8f9 (diff) |
Call initialize_clone with freeze: false if clone called with freeze: false
This makes it possible to initialize_clone to correctly not freeze
internal state if the freeze: false keyword is passed to clone.
If clone is called with freeze: true or no keyword, do not pass
a second argument to initialize_clone to keep backwards
compatibility.
This makes it so that external libraries that override
initialize_clone but do not support the freeze keyword will fail
with ArgumentError if passing freeze: false to clone. I think that
is better than the current behavior, which succeeds but results in
an unfrozen object with frozen internals.
Fix related issues in set and delegate in stdlib.
Fixes [Bug #14266]
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/2816
Diffstat (limited to 'object.c')
-rw-r--r-- | object.c | 52 |
1 files changed, 44 insertions, 8 deletions
@@ -476,11 +476,25 @@ mutable_obj_clone(VALUE obj, int kwfreeze) } init_copy(clone, obj); - rb_funcall(clone, id_init_clone, 1, obj); if (kwfreeze) { + rb_funcall(clone, id_init_clone, 1, obj); RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE; } + else { + static VALUE freeze_false_hash; + VALUE argv[2]; + if (!freeze_false_hash) { + freeze_false_hash = rb_hash_new(); + rb_hash_aset(freeze_false_hash, ID2SYM(rb_intern("freeze")), Qfalse); + rb_obj_freeze(freeze_false_hash); + rb_gc_register_mark_object(freeze_false_hash); + } + + argv[0] = obj; + argv[1] = freeze_false_hash; + rb_funcallv_kw(clone, id_init_clone, 2, argv, RB_PASS_CALLED_KEYWORDS); + } return clone; } @@ -637,10 +651,10 @@ rb_obj_init_copy(VALUE obj, VALUE orig) /*! * :nodoc: *-- - * Default implementation of \c #initialize_dup and \c #initialize_clone + * Default implementation of \c #initialize_dup * * \param[in,out] obj the receiver being initialized - * \param[in] orig the object to be dup or cloned from. + * \param[in] orig the object to be dup from. *++ **/ VALUE @@ -650,6 +664,27 @@ rb_obj_init_dup_clone(VALUE obj, VALUE orig) return obj; } +/*! + * :nodoc: + *-- + * Default implementation of \c #initialize_clone + * + * \param[in] The number of arguments + * \param[in] The array of arguments + * \param[in] obj the receiver being initialized + *++ + **/ +static VALUE +rb_obj_init_clone(int argc, VALUE *argv, VALUE obj) +{ + VALUE orig, opts; + rb_scan_args(argc, argv, "1:", &orig, &opts); + /* Ignore a freeze keyword */ + if (argc == 2) (void)freeze_opt(1, &opts); + rb_funcall(obj, id_init_copy, 1, orig); + return obj; +} + /** * call-seq: * obj.to_s -> string @@ -1968,10 +2003,11 @@ rb_mod_initialize(VALUE module) /* :nodoc: */ static VALUE -rb_mod_initialize_clone(VALUE clone, VALUE orig) +rb_mod_initialize_clone(int argc, VALUE* argv, VALUE clone) { - VALUE ret; - ret = rb_obj_init_dup_clone(clone, orig); + VALUE ret, orig, opts; + rb_scan_args(argc, argv, "1:", &orig, &opts); + ret = rb_obj_init_clone(argc, argv, clone); if (OBJ_FROZEN(orig)) rb_class_name(clone); return ret; @@ -4579,7 +4615,7 @@ InitVM_Object(void) rb_define_method(rb_mKernel, "then", rb_obj_yield_self, 0); rb_define_method(rb_mKernel, "initialize_copy", rb_obj_init_copy, 1); rb_define_method(rb_mKernel, "initialize_dup", rb_obj_init_dup_clone, 1); - rb_define_method(rb_mKernel, "initialize_clone", rb_obj_init_dup_clone, 1); + rb_define_method(rb_mKernel, "initialize_clone", rb_obj_init_clone, -1); rb_define_method(rb_mKernel, "taint", rb_obj_taint, 0); rb_define_method(rb_mKernel, "tainted?", rb_obj_tainted, 0); @@ -4666,7 +4702,7 @@ InitVM_Object(void) rb_define_alloc_func(rb_cModule, rb_module_s_alloc); rb_define_method(rb_cModule, "initialize", rb_mod_initialize, 0); - rb_define_method(rb_cModule, "initialize_clone", rb_mod_initialize_clone, 1); + rb_define_method(rb_cModule, "initialize_clone", rb_mod_initialize_clone, -1); rb_define_method(rb_cModule, "instance_methods", rb_class_instance_methods, -1); /* in class.c */ rb_define_method(rb_cModule, "public_instance_methods", rb_class_public_instance_methods, -1); /* in class.c */ |