irb(main):001:0> is = VM::InstructionSequence.compile('foo.bar = 42')
=> ISeq:maincompiled
irb(main):003:0> puts is.disasm
== disasm: <ISeq:<main>@<compiled>>=====================================
0000 putnil                                                           (   1)
0001 putnil           
0002 send             :foo, 0, nil, 24, <ic>
0008 putobject        42
0010 setn             2
0012 send             :bar=, 1, nil, 0, <ic>
0018 pop              
0019 leave            
=> nil

I'm a little confused about how the opcodes manipulate the stack.  This is what
I have written on paper:

putnil               #=> [ nil ]
putnil               #=> [ nil, nil ]
send :foo, 0         #=> [ nil, nil, result of 1st send ]
putobject 42         #=> [ nil, nil, result of 1st send, 42 ]
setn 42              #=> [ nil, 42, result of 1st send ]
send :bar=, 1        #=> [ nil, result of 2nd send ]
pop                  #=> [ nil ]

But if I try this in irb:

irb(main):008:0> is = VM::InstructionSequence.compile('foo = OpenStruct.new; foo.bar = 42')
=> ISeq:maincompiled
irb(main):009:0> is.eval
=> 42

The answer is 42, not nil.  Where is my misunderstanding?

Thanks,

Paul