From 24fa37d87a24dc932c1d778bcaf91204f5c12a76 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 3 Jul 2020 18:02:43 -0700 Subject: [PATCH] Make Kernel#then, #yield_self, #frozen? builtin (#3283) * Make Kernel#then, #yield_self, #frozen? builtin * Fix test_jit --- benchmark/kernel_then.yml | 6 ++ benchmark/{mjit_class.yml => mjit_kernel.yml} | 9 +++ kernel.rb | 78 +++++++++++++++++++ object.c | 55 +------------ test/ruby/test_jit.rb | 2 +- 5 files changed, 96 insertions(+), 54 deletions(-) create mode 100644 benchmark/kernel_then.yml rename benchmark/{mjit_class.yml => mjit_kernel.yml} (57%) diff --git a/benchmark/kernel_then.yml b/benchmark/kernel_then.yml new file mode 100644 index 0000000000..85f7341e33 --- /dev/null +++ b/benchmark/kernel_then.yml @@ -0,0 +1,6 @@ +benchmark: + kernel_then: 1.then { |i| i + 1 } + kernel_then_enum: 1.then + kernel_yield_self: 1.yield_self { |i| i + 1 } + kernel_yield_self_enum: 1.yield_self +loop_count: 20000000 diff --git a/benchmark/mjit_class.yml b/benchmark/mjit_kernel.yml similarity index 57% rename from benchmark/mjit_class.yml rename to benchmark/mjit_kernel.yml index 22f95c2d4d..7720e65c2c 100644 --- a/benchmark/mjit_class.yml +++ b/benchmark/mjit_kernel.yml @@ -4,8 +4,17 @@ prelude: | obj.class end + def mjit_frozen?(obj) + obj.frozen? + end + + str = "" + fstr = "".freeze + benchmark: - mjit_class(self) - mjit_class(1) + - mjit_frozen?(str) + - mjit_frozen?(fstr) loop_count: 40000000 diff --git a/kernel.rb b/kernel.rb index 3404053d84..04a371a7c8 100644 --- a/kernel.rb +++ b/kernel.rb @@ -48,6 +48,27 @@ module Kernel Primitive.rb_obj_clone2(freeze) end + # + # call-seq: + # obj.frozen? -> true or false + # + # Returns the freeze status of obj. + # + # a = [ "a", "b", "c" ] + # a.freeze #=> ["a", "b", "c"] + # a.frozen? #=> true + #-- + # Determines if the object is frozen. Equivalent to \c Object\#frozen? in Ruby. + # \param[in] obj the object to be determines + # \retval Qtrue if frozen + # \retval Qfalse if not frozen + #++ + # + def frozen? + Primitive.attr! 'inline' + Primitive.cexpr! 'rb_obj_frozen_p(self)' + end + # # call-seq: # obj.tap {|x| block } -> obj @@ -70,6 +91,63 @@ module Kernel self end + # + # call-seq: + # obj.then {|x| block } -> an_object + # + # Yields self to the block and returns the result of the block. + # + # 3.next.then {|x| x**x }.to_s #=> "256" + # + # Good usage for +then+ is value piping in method chains: + # + # require 'open-uri' + # require 'json' + # + # construct_url(arguments). + # then {|url| open(url).read }. + # then {|response| JSON.parse(response) } + # + # When called without block, the method returns +Enumerator+, + # which can be used, for example, for conditional + # circuit-breaking: + # + # # meets condition, no-op + # 1.then.detect(&:odd?) # => 1 + # # does not meet condition, drop value + # 2.then.detect(&:odd?) # => nil + # + def then + unless Primitive.block_given_p + return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, rb_obj_size)' + end + yield(self) + end + + # + # call-seq: + # obj.yield_self {|x| block } -> an_object + # + # Yields self to the block and returns the result of the block. + # + # "my string".yield_self {|s| s.upcase } #=> "MY STRING" + # + # Good usage for +then+ is value piping in method chains: + # + # require 'open-uri' + # require 'json' + # + # construct_url(arguments). + # then {|url| open(url).read }. + # then {|response| JSON.parse(response) } + # + def yield_self + unless Primitive.block_given_p + return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, rb_obj_size)' + end + yield(self) + end + module_function # diff --git a/object.c b/object.c index 4718fad66f..a684927d10 100644 --- a/object.c +++ b/object.c @@ -597,41 +597,10 @@ rb_obj_size(VALUE self, VALUE args, VALUE obj) return LONG2FIX(1); } -/* - * call-seq: - * obj.then {|x| block } -> an_object - * obj.yield_self {|x| block } -> an_object - * - * Yields self to the block and returns the result of the block. - * - * 3.next.then {|x| x**x }.to_s #=> "256" - * "my string".yield_self {|s| s.upcase } #=> "MY STRING" - * - * Good usage for +then+ is value piping in method chains: - * - * require 'open-uri' - * require 'json' - * - * construct_url(arguments). - * then {|url| open(url).read }. - * then {|response| JSON.parse(response) } - * - * When called without block, the method returns +Enumerator+, - * which can be used, for example, for conditional - * circuit-breaking: - * - * # meets condition, no-op - * 1.then.detect(&:odd?) # => 1 - * # does not meet condition, drop value - * 2.then.detect(&:odd?) # => nil - * - */ - static VALUE -rb_obj_yield_self(VALUE obj) +block_given_p(rb_execution_context_t *ec, VALUE self) { - RETURN_SIZED_ENUMERATOR(obj, 0, 0, rb_obj_size); - return rb_yield_values2(1, &obj); + return rb_block_given_p() ? Qtrue : Qfalse; } /** @@ -1321,23 +1290,6 @@ rb_obj_freeze(VALUE obj) return obj; } -/** - * call-seq: - * obj.frozen? -> true or false - * - * Returns the freeze status of obj. - * - * a = [ "a", "b", "c" ] - * a.freeze #=> ["a", "b", "c"] - * a.frozen? #=> true - *-- - * Determines if the object is frozen. Equivalent to \c Object\#frozen? in Ruby. - * \param[in] obj the object to be determines - * \retval Qtrue if frozen - * \retval Qfalse if not frozen - *++ - */ - VALUE rb_obj_frozen_p(VALUE obj) { @@ -4581,8 +4533,6 @@ InitVM_Object(void) rb_define_method(rb_mKernel, "singleton_class", rb_obj_singleton_class, 0); rb_define_method(rb_mKernel, "dup", rb_obj_dup, 0); rb_define_method(rb_mKernel, "itself", rb_obj_itself, 0); - rb_define_method(rb_mKernel, "yield_self", rb_obj_yield_self, 0); - 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_clone, -1); @@ -4594,7 +4544,6 @@ InitVM_Object(void) rb_define_method(rb_mKernel, "untrusted?", rb_obj_untrusted, 0); rb_define_method(rb_mKernel, "trust", rb_obj_trust, 0); rb_define_method(rb_mKernel, "freeze", rb_obj_freeze, 0); - rb_define_method(rb_mKernel, "frozen?", rb_obj_frozen_p, 0); rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0); rb_define_method(rb_mKernel, "inspect", rb_obj_inspect, 0); diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb index 339bfa2193..f929a8c851 100644 --- a/test/ruby/test_jit.rb +++ b/test/ruby/test_jit.rb @@ -372,7 +372,7 @@ class TestJIT < Test::Unit::TestCase end def test_compile_insn_send - assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 2, insns: %i[send]) + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 3, insns: %i[send]) begin; print proc { yield_self { 1 } }.call end;