Ruby Keyword Argument Implementation Differences Between 2.1 and 2.2

I'm currently reading through Ruby Under a Microscope: An Illustrated Guide to Ruby Internals, and when I tried to test the "hidden hash" in ruby 2.2, I got different results from the book.

In Ruby 2.2, it appears that the default arguments are used as one expects, without regard to the Hash monkey patch:

### CODE ###
class Hash
  def key?(val)
    false
  end
end
 
def blah(a: 2, b: 5)
  puts a + b
end
blah
blah(a:3)
blah(b:3)
blah(a:2000, b:2000)
 
### DISASSEMBLY ###
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace            1                                               (   1)
0002 putspecialobject 3
0004 putnil
0005 defineclass      :Hash, <class:Hash>, 0
0009 pop
0010 trace            1                                               (   7)
0012 putspecialobject 1
0014 putspecialobject 2
0016 putobject        :blah
0018 putiseq          blah
0020 opt_send_without_block <callinfo!mid:core#define_method, argc:3, ARGS_SIMPLE>
0022 pop
0023 trace            1                                               (  10)
0025 putself
0026 opt_send_without_block <callinfo!mid:blah, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0028 pop
0029 trace            1                                               (  11)
0031 putself
0032 putobject        3
0034 opt_send_without_block <callinfo!mid:blah, argc:1, kw:1, FCALL>
0036 pop
0037 trace            1                                               (  12)
0039 putself
0040 putobject        3
0042 opt_send_without_block <callinfo!mid:blah, argc:1, kw:1, FCALL>
0044 pop
0045 trace            1                                               (  13)
0047 putself
0048 putobject        2000
0050 putobject        2000
0052 opt_send_without_block <callinfo!mid:blah, argc:2, kw:2, FCALL>
0054 leave
== disasm: <RubyVM::InstructionSequence:<class:Hash>@<compiled>>========
0000 trace            2                                               (   1)
0002 trace            1                                               (   2)
0004 putspecialobject 1
0006 putspecialobject 2
0008 putobject        :key?
0010 putiseq          key?
0012 opt_send_without_block <callinfo!mid:core#define_method, argc:3, ARGS_SIMPLE>
0014 trace            4                                               (   5)
0016 leave                                                            (   2)
== disasm: <RubyVM::InstructionSequence:key?@<compiled>>================
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] val<Arg>
0000 trace            8                                               (   2)
0002 trace            1                                               (   3)
0004 putobject        false
0006 trace            16                                              (   4)
0008 leave                                                            (   3)
== disasm: <RubyVM::InstructionSequence:blah@<compiled>>================
local table (size: 4, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: 2@0, kwrest: -1])
[ 4] a          [ 3] b          [ 2] ?
0000 trace            8                                               (   7)
0002 trace            1                                               (   8)
0004 putself
0005 getlocal_OP__WC__0 4
0007 getlocal_OP__WC__0 3
0009 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>
0011 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0013 trace            16                                              (   9)
0015 leave                                                            (   8)
 
### RESULTS ###
7
8
5
4000

If I reset back to ruby 2.1, I get the unconditional usage of the default arguments:

### CODE ###
class Hash
  def key?(val)
    false
  end
end
 
def blah(a: 2, b: 5)
  puts a + b
end
blah
blah(a:3)
blah(b:3)
blah(a:2000, b:2000)
 
### DISASSEMBLY ###
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace            1                                               (   1)
0002 putspecialobject 3
0004 putnil
0005 defineclass      :Hash, <class:Hash>, 0
0009 pop
0010 trace            1                                               (   7)
0012 putspecialobject 1
0014 putspecialobject 2
0016 putobject        :blah
0018 putiseq          blah
0020 opt_send_simple  <callinfo!mid:core#define_method, argc:3, ARGS_SKIP>
0022 pop
0023 trace            1                                               (  10)
0025 putself
0026 opt_send_simple  <callinfo!mid:blah, argc:0, FCALL|VCALL|ARGS_SKIP>
0028 pop
0029 trace            1                                               (  11)
0031 putself
0032 putspecialobject 1
0034 putobject        [:a, 3]
0036 opt_send_simple  <callinfo!mid:core#hash_from_ary, argc:1, ARGS_SKIP>
0038 opt_send_simple  <callinfo!mid:blah, argc:1, FCALL|ARGS_SKIP>
0040 pop
0041 trace            1                                               (  12)
0043 putself
0044 putspecialobject 1
0046 putobject        [:b, 3]
0048 opt_send_simple  <callinfo!mid:core#hash_from_ary, argc:1, ARGS_SKIP>
0050 opt_send_simple  <callinfo!mid:blah, argc:1, FCALL|ARGS_SKIP>
0052 pop
0053 trace            1                                               (  13)
0055 putself
0056 putspecialobject 1
0058 putobject        [:a, 2000, :b, 2000]
0060 opt_send_simple  <callinfo!mid:core#hash_from_ary, argc:1, ARGS_SKIP>
0062 opt_send_simple  <callinfo!mid:blah, argc:1, FCALL|ARGS_SKIP>
0064 leave
== disasm: <RubyVM::InstructionSequence:<class:Hash>@<compiled>>========
0000 trace            2                                               (   1)
0002 trace            1                                               (   2)
0004 putspecialobject 1
0006 putspecialobject 2
0008 putobject        :key?
0010 putiseq          key?
0012 opt_send_simple  <callinfo!mid:core#define_method, argc:3, ARGS_SKIP>
0014 trace            4                                               (   5)
0016 leave                                                            (   2)
== disasm: <RubyVM::InstructionSequence:key?@<compiled>>================
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] val<Arg>
0000 trace            8                                               (   2)
0002 trace            1                                               (   3)
0004 putobject        false
0006 trace            16                                              (   4)
0008 leave                                                            (   3)
== disasm: <RubyVM::InstructionSequence:blah@<compiled>>================
local table (size: 4, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 2@2] s0)
[ 4] a          [ 3] b          [ 2] ?
0000 getlocal_OP__WC__0 2                                             (   7)
0002 dup
0003 putobject        :a
0005 opt_send_simple  <callinfo!mid:key?, argc:1, ARGS_SKIP>
0007 branchunless     18
0009 dup
0010 putobject        :a
0012 opt_send_simple  <callinfo!mid:delete, argc:1, ARGS_SKIP>
0014 setlocal_OP__WC__0 4
0016 jump             22
0018 putobject        2
0020 setlocal_OP__WC__0 4
0022 dup
0023 putobject        :b
0025 opt_send_simple  <callinfo!mid:key?, argc:1, ARGS_SKIP>
0027 branchunless     38
0029 dup
0030 putobject        :b
0032 opt_send_simple  <callinfo!mid:delete, argc:1, ARGS_SKIP>
0034 setlocal_OP__WC__0 3
0036 jump             42
0038 putobject        5
0040 setlocal_OP__WC__0 3
0042 pop
0043 trace            8
0045 trace            1                                               (   8)
0047 putself
0048 getlocal_OP__WC__0 4
0050 getlocal_OP__WC__0 3
0052 opt_plus         <callinfo!mid:+, argc:1, ARGS_SKIP>
0054 opt_send_simple  <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0056 trace            16                                              (   9)
0058 leave                                                            (   8)
 
### RESULTS ###
7
7
7
7