diff --git a/compile.c b/compile.c index fc6453a..51bc18f 100644 --- a/compile.c +++ b/compile.c @@ -1872,6 +1872,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) switch (mid) { case idLength: SP_INSN(length); break; case idSize: SP_INSN(size); break; + case idEmptyP: SP_INSN(empty_p);break; case idSucc: SP_INSN(succ); break; case idNot: SP_INSN(not); break; } diff --git a/id.c b/id.c index e5f971d..b614397 100644 --- a/id.c +++ b/id.c @@ -41,6 +41,7 @@ Init_id(void) REGISTER_SYMID(idEach, "each"); REGISTER_SYMID(idLength, "length"); REGISTER_SYMID(idSize, "size"); + REGISTER_SYMID(idEmptyP, "empty?"); REGISTER_SYMID(idLambda, "lambda"); REGISTER_SYMID(idIntern, "intern"); REGISTER_SYMID(idGets, "gets"); diff --git a/insns.def b/insns.def index e55ec30..495452f 100644 --- a/insns.def +++ b/insns.def @@ -1991,6 +1991,44 @@ opt_size /** @c optimize + @e optimized empty? + @j 最適化された recv.empty?()。 + */ +DEFINE_INSN +opt_empty_p +(IC ic) +(VALUE recv) +(VALUE val) +{ + if (!SPECIAL_CONST_P(recv)) { + if (HEAP_CLASS_OF(recv) == rb_cString && + BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, STRING_REDEFINED_OP_FLAG)) { + if (RSTRING_LEN(recv) == 0) val = Qtrue; + else val = Qfalse; + } + else if (HEAP_CLASS_OF(recv) == rb_cArray && + BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, ARRAY_REDEFINED_OP_FLAG)) { + if (RARRAY_LEN(recv) == 0) val = Qtrue; + else val = Qfalse; + } + else if (HEAP_CLASS_OF(recv) == rb_cHash && + BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, HASH_REDEFINED_OP_FLAG)) { + if (RHASH_EMPTY_P(recv)) val = Qtrue; + else val = Qfalse; + } + else { + goto INSN_LABEL(normal_dispatch); + } + } + else { + INSN_LABEL(normal_dispatch): + PUSH(recv); + CALL_SIMPLE_METHOD(0, idEmptyP, recv); + } +} + +/** + @c optimize @e optimized succ @j 最適化された recv.succ()。 */ diff --git a/template/id.h.tmpl b/template/id.h.tmpl index 0e54e76..f7e764c 100644 --- a/template/id.h.tmpl +++ b/template/id.h.tmpl @@ -20,6 +20,7 @@ method_ids = %w[ MethodMissing Length Size + EmptyP Gets Succ Each diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb index 1b0c730..a3b4aed 100644 --- a/test/ruby/test_optimization.rb +++ b/test/ruby/test_optimization.rb @@ -85,6 +85,14 @@ class TestRubyOptimization < Test::Unit::TestCase assert_equal 6, "string".length end + def test_string_empty? + assert_equal true, "".empty? + assert_equal false, "string".empty? + assert_nil redefine_method('String', 'empty?') { "string".empty? } + assert_equal true, "".empty? + assert_equal false, "string".empty? + end + def test_string_plus assert_equal "", "" + "" assert_equal "x", "x" + "" @@ -116,11 +124,21 @@ class TestRubyOptimization < Test::Unit::TestCase assert_equal 3, [1,2,3].length end + def test_array_empty? + assert_equal true, [].empty? + assert_equal false, [1,2,3].empty? + end + def test_hash_length assert_equal 0, {}.length assert_equal 1, {1=>1}.length end + def test_hash_empty? + assert_equal true, {}.empty? + assert_equal false, {1=>1}.empty? + end + class MyObj def ==(other) true diff --git a/vm.c b/vm.c index 55ccfe7..d5a8d15 100644 --- a/vm.c +++ b/vm.c @@ -1005,6 +1005,7 @@ vm_init_redefined_flag(void) OP(ASET, ASET), (C(Array), C(Hash)); OP(Length, LENGTH), (C(Array), C(String), C(Hash)); OP(Size, SIZE), (C(Array), C(String), C(Hash)); + OP(EmptyP, EMPTY_P), (C(Array), C(String), C(Hash)); OP(Succ, SUCC), (C(Fixnum), C(String), C(Time)); #undef C #undef OP diff --git a/vm_insnhelper.h b/vm_insnhelper.h index 4d5f0e3..7c7745a 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -49,6 +49,7 @@ enum { BOP_ASET, BOP_LENGTH, BOP_SIZE, + BOP_EMPTY_P, BOP_SUCC, BOP_GT, BOP_GE,