Ruby closures
How are they possible?
Carlos Alonso
Software Engineer
@calonso
A story about curiosity…
I am curious
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
# => hello
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
# => hello
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
# => hello
This talk!
Some examples…
a = 1
def sum
a + 1
end
sum
a = 1
def sum
a + 1
end
sum
NameError: Undefined `a’
def run_block
yield
end
a = 1
run_block { a + 1 }
def run_block
yield
end
a = 1
run_block { a + 1 }
2
def run_block
a = 1
yield
end
run_block { a + 1 }
def run_block
a = 1
yield
end
run_block { a + 1 }
NameError: Undefined `a’
def run_block
a = 100
yield
end
a = 1
run_block { a + 1 }
def run_block
a = 100
yield
end
a = 1
run_block { a + 1 }
2
a = 1
sum = lambda do
a + 1
end
a = 2
sum.call
a = 1
sum = lambda do
a + 1
end
a = 2
sum.call
3
What is a closure?
A lambda expression
+
an environment to run it
Blocks refer to variables declared in
the scope where they were defined,
not where they are executed.
Lexical scope
Ruby uses lexical scope.
Let’s see this scopes!!
a = 1
def sum
a + 1
end
sum
a = 1
def sum
a + 1
end
sum
top-level
a = 1
sum = …
a = 1
def sum
a + 1
end
sum
top-level
a = 1
sum = …
sum
a??
a = 1
def sum
a + 1
end
sum
top-level
a = 1
sum = …
sum
a??
NameError: Undefined `a’
def run_block(&block)
yield
end
a = 1
run_block { a + 1 }
def run_block(&block)
yield
end
a = 1
run_block { a + 1 }
top-level
run_block = …
a = 1
def run_block(&block)
yield
end
a = 1
run_block { a + 1 }
top-level
run_block = …
a = 1 run_block
block
def run_block(&block)
yield
end
a = 1
run_block { a + 1 }
top-level
run_block = …
a = 1
in-block
a
run_block
block
def run_block(&block)
yield
end
a = 1
run_block { a + 1 }
top-level
run_block = …
a = 1
in-block
a
run_block
block
2
def run_block(&block)
a = 1
yield
end
run_block { a + 1 }
def run_block(&block)
a = 1
yield
end
run_block { a + 1 }
top-level
run_block = …
def run_block(&block)
a = 1
yield
end
run_block { a + 1 }
top-level
run_block = …
run_block
a = 1
block
def run_block(&block)
a = 1
yield
end
run_block { a + 1 }
top-level
run_block = …
in-block
a??
run_block
a = 1
block
def run_block(&block)
a = 1
yield
end
run_block { a + 1 }
top-level
run_block = …
in-block
a??
run_block
a = 1
block
NameError: Undefined `a’
def run_block(&block)
a = 100
yield
end
a = 1
run_block { a + 1 }
def run_block(&block)
a = 100
yield
end
a = 1
run_block { a + 1 }
top-level
run_block = …
a = 1
def run_block(&block)
a = 100
yield
end
a = 1
run_block { a + 1 }
top-level
run_block = …
a = 1 run_block
a = 100
block
def run_block(&block)
a = 100
yield
end
a = 1
run_block { a + 1 }
top-level
run_block = …
a = 1
in-block
a
run_block
a = 100
block
def run_block(&block)
a = 100
yield
end
a = 1
run_block { a + 1 }
top-level
run_block = …
a = 1
in-block
a
run_block
a = 100
block
2
a = 1
sum = lambda do
a + 1
end
a = 2
sum.call
a = 1
sum = lambda do
a + 1
end
a = 2
sum.call
top-level
a = 2
sum…
a = 1
sum = lambda do
a + 1
end
a = 2
sum.call
top-level
a = 2
sum…
in-lambda
a
a = 1
sum = lambda do
a + 1
end
a = 2
sum.call
top-level
a = 2
sum…
in-lambda
a
3
Cool!!
The lexical scope explains
the examples
Cool!!
The lexical scope explains
the examples
But… how exactly??
Ok, let’s start from scratch.
How does Ruby executes programs?
0002 putself
0003 putobject 2
0005 putobject 2
0007 opt_plus
0009 opt_send_simple puts
0011 leave
2
rb_control_frame_t
pc
sp
self
type
YARV internal
stack
[TOP LEVEL]
self
2
YARV Control
structures stack
YARV Instructions
CFP
https://github.com/ruby/ruby/blob/trunk/vm_core.h
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace 1 ( 1)
0002 putspecialobject 1
0004 putspecialobject 2
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple <callinfo!mid:core#define_method, argc:3, ARGS_SKIP>
0012 pop
0013 trace 1 ( 4)
0015 putself
0016 opt_send_simple <callinfo!mid:greet, argc:0, FCALL|VCALL|ARGS_SKIP>
0018 leave
== disasm: <RubyVM::InstructionSequence:greet@<compiled>>===============
0000 trace 8 ( 1)
0002 trace 1 ( 2)
0004 putself
0005 putstring "hello"
0007 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0009 trace 16 ( 3)
0011 leave ( 2)
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
greet
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
greet
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
rb_control_frame_t
pc
sp
self
type
[METHOD]
0004 putself
0005 putstring "hello"
0007 opt_send_simple puts
0011 leave
svar/cref
special
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
self
rb_control_frame_t
pc
sp
self
type
[METHOD]
0004 putself
0005 putstring "hello"
0007 opt_send_simple puts
0011 leave
svar/cref
special
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
self
rb_control_frame_t
pc
sp
self
type
[METHOD]
0004 putself
0005 putstring "hello"
0007 opt_send_simple puts
0011 leave
svar/cref
special
“hello”
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
self
rb_control_frame_t
pc
sp
self
type
[METHOD]
0004 putself
0005 putstring "hello"
0007 opt_send_simple puts
0011 leave
svar/cref
special
$ “hello”
def greet
puts “hello”
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
def greet
puts “hello”
end
greet
Ok cool, what if the method has
local variables?
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace 1 ( 1)
0002 putspecialobject 1
0004 putspecialobject 2
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple <callinfo!mid:core#define_method, argc:3, ARGS_SKIP>
0012 pop
0013 trace 1 ( 5)
0015 putself
0016 opt_send_simple <callinfo!mid:greet, argc:0, FCALL|VCALL|ARGS_SKIP>
0018 leave
== disasm: <RubyVM::InstructionSequence:greet@<compiled>>===============
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] str
0000 trace 8 ( 1)
0002 trace 1 ( 2)
0004 putstring "hello"
0006 setlocal_OP__WC__0 2
0008 trace 1 ( 3)
0010 putself
0011 getlocal_OP__WC__0 2
0013 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0015 trace 16 ( 4)
0017 leave( 3)
def greet
str = “hello”
puts str
end
greet
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] str
The local table
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
def greet
str = “hello”
puts str
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 getlocal 2, 0
0013 opt_send_simple puts
0017 leave
str
svar/cref
ep
def greet
str = “hello”
puts str
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
str
svar/cref
“hello”
ep
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 getlocal 2, 0
0013 opt_send_simple puts
0017 leave
def greet
str = “hello”
puts str
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
str
svar/cref
ep
“hello”
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 getlocal 2, 0
0013 opt_send_simple puts
0017 leave
def greet
str = “hello”
puts str
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
str
svar/cref
ep
self
“hello”
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 getlocal 2, 0
0013 opt_send_simple puts
0017 leave
def greet
str = “hello”
puts str
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
str
svar/cref
ep
self
“hello”
“hello”
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 getlocal 2, 0
0013 opt_send_simple puts
0017 leave
def greet
str = “hello”
puts str
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
str
svar/cref
ep
“hello”
$ “hello”
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 getlocal 2, 0
0013 opt_send_simple puts
0017 leave
def greet
str = “hello”
puts str
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
$ “hello”
def greet
str = “hello”
puts str
end
greet
Got it, but what about blocks
accessing variables on lexical
scope?
………
== disasm: <RubyVM::InstructionSequence:greet@<compiled>>===============
== catch table
| catch type: break st: 0010 ed: 0014 sp: 0000 cont: 0014
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] str
0000 trace 8 ( 1)
0002 trace 1 ( 2)
0004 putstring "hello"
0006 setlocal_OP__WC__0 2
0008 trace 1 ( 3)
0010 putobject 3
0012 send <callinfo!mid:times, argc:0, block:block in greet>
0014 trace 16 ( 6)
0016 leave ( 3)
== disasm: <RubyVM::InstructionSequence:block in greet@<compiled>>======
== catch table
| catch type: redo st: 0002 ed: 0009 sp: 0000 cont: 0002
| catch type: next st: 0002 ed: 0009 sp: 0000 cont: 0009
|------------------------------------------------------------------------
0000 trace 256 ( 3)
0002 trace 1 ( 4)
0004 putself
0005 getlocal_OP__WC__1 2
0007 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0009 trace 512 ( 5)
0011 leave ( 4)
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
str
svar/cref
ep
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
str
svar/cref
“hello”
ep
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
str
svar/cref
ep
“hello”
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
self
type
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
self
type
[METHOD]
str
svar/cref
ep
3
“hello”
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
[METHOD]
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
str
svar/cref
ep
3
“hello”
rb_block_t
iseq
ep
…
0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
[METHOD]
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
str
svar/cref
ep
3
“hello”
svar/cref
special
rb_block_t
iseq
ep
…
rb_control_frame_t
pc
sp
[METHOD]
ep
times…
0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
[METHOD]
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
str
svar/cref
ep
3
“hello”
svar/cref
special
svar/cref
rb_block_t
iseq
ep
…
rb_control_frame_t
pc
sp
[METHOD]
ep
times…
rb_con…
pc
sp
[BLOCK]
ep
special 0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
[METHOD]
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
str
svar/cref
ep
3
“hello”
svar/cref
special
svar/cref
rb_block_t
iseq
ep
…
rb_control_frame_t
pc
sp
[METHOD]
ep
times…
rb_con…
pc
sp
[BLOCK]
ep
special
self
0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
[METHOD]
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
str
svar/cref
ep
3
“hello”
svar/cref
special
svar/cref
rb_block_t
iseq
ep
…
rb_control_frame_t
pc
sp
[METHOD]
ep
times…
rb_con…
pc
sp
[BLOCK]
ep
0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
special
self
“hello”
def greet
str = “hello”
3.times do
puts str
end
end
greet
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 leave
rb_control_frame_t
pc
sp
[TOP LEVEL]
:greet
self
special
rb_control_frame_t
pc
sp
[METHOD]
0004 putstring "hello"
0006 setlocal 2, 0
0010 putobject 3
0012 send times
0016 leave
str
svar/cref
ep
3
“hello”
svar/cref
special
svar/cref
rb_block_t
iseq
ep
…
rb_control_frame_t
pc
sp
[METHOD]
ep
times…
rb_con…
pc
sp
[BLOCK]
ep
0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
special
self
$ “hello”
def greet
str = “hello”
3.times do
puts str
end
end
greet
https://github.com/ruby/ruby/blob/trunk/vm_core.h
And what if that scope doesn’t
exist anymore?
The heap and
rb_proc_t to the
rescue!
o/
Stack vs Heap
:greet
self
str
“hello”
RString
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] f
0002 putspecialobject 1
0004 putspecialobject 2
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple <callinfo!mid:core#define_method, argc:3, ARGS_SKIP>
0012 pop
0015 putself
0016 opt_send_simple <callinfo!mid:greet, argc:0, FCALL|VCALL|ARGS_SKIP>
0018 setlocal_OP__WC__0 2
0022 getlocal_OP__WC__0 2
0024 opt_send_simple <callinfo!mid:call, argc:0, ARGS_SKIP>
0026 leave
== disasm: <RubyVM::InstructionSequence:greet@<compiled>>===============
== catch table
| catch type: break st: 0010 ed: 0013 sp: 0000 cont: 0013
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] str
0004 putstring "hello"
0006 setlocal_OP__WC__0 2
0010 putself
0011 send <callinfo!mid:lambda, argc:0, block:block in greet, FCALL>
0015 leave ( 3)
== disasm: <RubyVM::InstructionSequence:block in greet@<compiled>>======
== catch table
| catch type: redo st: 0002 ed: 0009 sp: 0000 cont: 0002
| catch type: next st: 0002 ed: 0009 sp: 0000 cont: 0009
|------------------------------------------------------------------------
0004 putself
0005 getlocal_OP__WC__1 2
0007 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0011 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
rb_control_frame_t
pc
sp
[TOP LEVEL]
ep
rb_control_frame_t
pc
sp
[METHOD]
ep
f
svar/cref
special
:greet
self
str
svar/cref
special
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
rb_control_frame_t
pc
sp
[TOP LEVEL]
ep
rb_control_frame_t
pc
sp
[METHOD]
ep
f
svar/cref
special
:greet
self
str
svar/cref
special
“hello”
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
ep
rb_control_frame_t
pc
sp
[METHOD]
ep
f
svar/cref
special
:greet
self
str
svar/cref
special
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
ep
rb_control_frame_t
pc
sp
[METHOD]
ep
f
svar/cref
special
:greet
self
str
svar/cref
special
self
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
str
rb_control_frame_t
pc
sp
[METHOD]
epsvar/cref
special
self
lambda
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello” str
svar/cref
special
self
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
str
rb_control_frame_t
pc
sp
[METHOD]
epsvar/cref
special
self
lambda
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
str
svar/cref
special
self
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
str
rb_control_frame_t
pc
sp
[METHOD]
epsvar/cref
special
self
lambda
rb_proc_t
rb_block_t
EP
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
str
svar/cref
special
self
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
str
rb_control_frame_t
pc
sp
[METHOD]
epsvar/cref
special
self
lambda
rb_proc_t
rb_block_t
EP
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
str
svar/cref
special
self
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
str
rb_control_frame_t
pc
sp
[METHOD]
epsvar/cref
special
self
lambda
rb_proc_t
rb_block_t
EP
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
0004 putstring "hello"
0006 setlocal 2, 0
0010 putself
0011 send lambda
0015 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
str
svar/cref
special
self
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
lambda
str
svar/cref
special
selfrb_proc_t
rb_block_t
EP
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
str
svar/cref
special
selfrb_proc_t
rb_block_t
EP
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
lambda
str
svar/cref
special
selfrb_proc_t
rb_block_t
EP
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
lambda
str
svar/cref
special
selfrb_proc_t
rb_block_t
EP
svar/cref
rb_control_frame_t
pc
sp
[BLOCK]
ep
0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
special
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
lambda
str
svar/cref
special
selfrb_proc_t
rb_block_t
EP
svar/cref
rb_control_frame_t
pc
sp
[BLOCK]
ep
special
self
0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
rb_control_frame_t
pc
sp
[TOP LEVEL]
epf
svar/cref
special
:greet
self
lambda
str
svar/cref
special
selfrb_proc_t
rb_block_t
EP
svar/cref
rb_control_frame_t
pc
sp
[BLOCK]
ep
special
self
“hello”
0004 putself
0005 getlocal 2, 1
0007 opt_send_simple puts
0011 leave
[ 2] f
0006 putobject :greet
0008 putiseq greet
0010 opt_send_simple core#define_method
0012 pop
0015 putself
0016 opt_send_simple greet
0018 setlocal 2, 0
0022 getlocal 2, 0
0024 opt_send_simple call
0026 leave
def greet
str = “hello”
lambda do
puts str
end
end
f = greet
f.call
“hello”
$ “hello”
https://github.com/ruby/ruby/blob/trunk/vm_core.h
Acknowledgements
Pat Shaughnessy
Questions?
Thanks!
Carlos Alonso
Software Engineer
@calonso

Ruby closures, how are they possible?

  • 1.
    Ruby closures How arethey possible? Carlos Alonso Software Engineer @calonso
  • 2.
    A story aboutcuriosity…
  • 3.
  • 7.
    def greet str =“hello” lambda do puts str end end f = greet f.call # => hello
  • 8.
    def greet str =“hello” lambda do puts str end end f = greet f.call # => hello
  • 9.
    def greet str =“hello” lambda do puts str end end f = greet f.call # => hello This talk!
  • 10.
  • 11.
    a = 1 defsum a + 1 end sum
  • 12.
    a = 1 defsum a + 1 end sum NameError: Undefined `a’
  • 13.
    def run_block yield end a =1 run_block { a + 1 }
  • 14.
    def run_block yield end a =1 run_block { a + 1 } 2
  • 15.
    def run_block a =1 yield end run_block { a + 1 }
  • 16.
    def run_block a =1 yield end run_block { a + 1 } NameError: Undefined `a’
  • 17.
    def run_block a =100 yield end a = 1 run_block { a + 1 }
  • 18.
    def run_block a =100 yield end a = 1 run_block { a + 1 } 2
  • 19.
    a = 1 sum= lambda do a + 1 end a = 2 sum.call
  • 20.
    a = 1 sum= lambda do a + 1 end a = 2 sum.call 3
  • 22.
    What is aclosure? A lambda expression + an environment to run it
  • 23.
    Blocks refer tovariables declared in the scope where they were defined, not where they are executed. Lexical scope
  • 24.
    Ruby uses lexicalscope. Let’s see this scopes!!
  • 25.
    a = 1 defsum a + 1 end sum
  • 26.
    a = 1 defsum a + 1 end sum top-level a = 1 sum = …
  • 27.
    a = 1 defsum a + 1 end sum top-level a = 1 sum = … sum a??
  • 28.
    a = 1 defsum a + 1 end sum top-level a = 1 sum = … sum a?? NameError: Undefined `a’
  • 29.
  • 30.
    def run_block(&block) yield end a =1 run_block { a + 1 } top-level run_block = … a = 1
  • 31.
    def run_block(&block) yield end a =1 run_block { a + 1 } top-level run_block = … a = 1 run_block block
  • 32.
    def run_block(&block) yield end a =1 run_block { a + 1 } top-level run_block = … a = 1 in-block a run_block block
  • 33.
    def run_block(&block) yield end a =1 run_block { a + 1 } top-level run_block = … a = 1 in-block a run_block block 2
  • 34.
    def run_block(&block) a =1 yield end run_block { a + 1 }
  • 35.
    def run_block(&block) a =1 yield end run_block { a + 1 } top-level run_block = …
  • 36.
    def run_block(&block) a =1 yield end run_block { a + 1 } top-level run_block = … run_block a = 1 block
  • 37.
    def run_block(&block) a =1 yield end run_block { a + 1 } top-level run_block = … in-block a?? run_block a = 1 block
  • 38.
    def run_block(&block) a =1 yield end run_block { a + 1 } top-level run_block = … in-block a?? run_block a = 1 block NameError: Undefined `a’
  • 39.
    def run_block(&block) a =100 yield end a = 1 run_block { a + 1 }
  • 40.
    def run_block(&block) a =100 yield end a = 1 run_block { a + 1 } top-level run_block = … a = 1
  • 41.
    def run_block(&block) a =100 yield end a = 1 run_block { a + 1 } top-level run_block = … a = 1 run_block a = 100 block
  • 42.
    def run_block(&block) a =100 yield end a = 1 run_block { a + 1 } top-level run_block = … a = 1 in-block a run_block a = 100 block
  • 43.
    def run_block(&block) a =100 yield end a = 1 run_block { a + 1 } top-level run_block = … a = 1 in-block a run_block a = 100 block 2
  • 44.
    a = 1 sum= lambda do a + 1 end a = 2 sum.call
  • 45.
    a = 1 sum= lambda do a + 1 end a = 2 sum.call top-level a = 2 sum…
  • 46.
    a = 1 sum= lambda do a + 1 end a = 2 sum.call top-level a = 2 sum… in-lambda a
  • 47.
    a = 1 sum= lambda do a + 1 end a = 2 sum.call top-level a = 2 sum… in-lambda a 3
  • 48.
    Cool!! The lexical scopeexplains the examples
  • 49.
    Cool!! The lexical scopeexplains the examples But… how exactly??
  • 50.
    Ok, let’s startfrom scratch. How does Ruby executes programs?
  • 52.
    0002 putself 0003 putobject2 0005 putobject 2 0007 opt_plus 0009 opt_send_simple puts 0011 leave 2 rb_control_frame_t pc sp self type YARV internal stack [TOP LEVEL] self 2 YARV Control structures stack YARV Instructions CFP
  • 53.
  • 54.
    == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>========== 0000trace 1 ( 1) 0002 putspecialobject 1 0004 putspecialobject 2 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple <callinfo!mid:core#define_method, argc:3, ARGS_SKIP> 0012 pop 0013 trace 1 ( 4) 0015 putself 0016 opt_send_simple <callinfo!mid:greet, argc:0, FCALL|VCALL|ARGS_SKIP> 0018 leave == disasm: <RubyVM::InstructionSequence:greet@<compiled>>=============== 0000 trace 8 ( 1) 0002 trace 1 ( 2) 0004 putself 0005 putstring "hello" 0007 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP> 0009 trace 16 ( 3) 0011 leave ( 2) def greet puts “hello” end greet
  • 55.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] def greet puts “hello” end greet
  • 56.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet def greet puts “hello” end greet
  • 57.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet greet def greet puts “hello” end greet
  • 58.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet greet def greet puts “hello” end greet
  • 59.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet def greet puts “hello” end greet
  • 60.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self def greet puts “hello” end greet
  • 61.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self rb_control_frame_t pc sp self type [METHOD] 0004 putself 0005 putstring "hello" 0007 opt_send_simple puts 0011 leave svar/cref special def greet puts “hello” end greet
  • 62.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self self rb_control_frame_t pc sp self type [METHOD] 0004 putself 0005 putstring "hello" 0007 opt_send_simple puts 0011 leave svar/cref special def greet puts “hello” end greet
  • 63.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self self rb_control_frame_t pc sp self type [METHOD] 0004 putself 0005 putstring "hello" 0007 opt_send_simple puts 0011 leave svar/cref special “hello” def greet puts “hello” end greet
  • 64.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self self rb_control_frame_t pc sp self type [METHOD] 0004 putself 0005 putstring "hello" 0007 opt_send_simple puts 0011 leave svar/cref special $ “hello” def greet puts “hello” end greet
  • 65.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self def greet puts “hello” end greet
  • 66.
    Ok cool, whatif the method has local variables?
  • 67.
    == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>========== 0000trace 1 ( 1) 0002 putspecialobject 1 0004 putspecialobject 2 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple <callinfo!mid:core#define_method, argc:3, ARGS_SKIP> 0012 pop 0013 trace 1 ( 5) 0015 putself 0016 opt_send_simple <callinfo!mid:greet, argc:0, FCALL|VCALL|ARGS_SKIP> 0018 leave == disasm: <RubyVM::InstructionSequence:greet@<compiled>>=============== local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1) [ 2] str 0000 trace 8 ( 1) 0002 trace 1 ( 2) 0004 putstring "hello" 0006 setlocal_OP__WC__0 2 0008 trace 1 ( 3) 0010 putself 0011 getlocal_OP__WC__0 2 0013 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP> 0015 trace 16 ( 4) 0017 leave( 3) def greet str = “hello” puts str end greet
  • 68.
    local table (size:2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1) [ 2] str The local table
  • 69.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self def greet str = “hello” puts str end greet
  • 70.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 getlocal 2, 0 0013 opt_send_simple puts 0017 leave str svar/cref ep def greet str = “hello” puts str end greet
  • 71.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] str svar/cref “hello” ep 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 getlocal 2, 0 0013 opt_send_simple puts 0017 leave def greet str = “hello” puts str end greet
  • 72.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] str svar/cref ep “hello” 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 getlocal 2, 0 0013 opt_send_simple puts 0017 leave def greet str = “hello” puts str end greet
  • 73.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] str svar/cref ep self “hello” 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 getlocal 2, 0 0013 opt_send_simple puts 0017 leave def greet str = “hello” puts str end greet
  • 74.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] str svar/cref ep self “hello” “hello” 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 getlocal 2, 0 0013 opt_send_simple puts 0017 leave def greet str = “hello” puts str end greet
  • 75.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] str svar/cref ep “hello” $ “hello” 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 getlocal 2, 0 0013 opt_send_simple puts 0017 leave def greet str = “hello” puts str end greet
  • 76.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self $ “hello” def greet str = “hello” puts str end greet
  • 77.
    Got it, butwhat about blocks accessing variables on lexical scope?
  • 78.
    ……… == disasm: <RubyVM::InstructionSequence:greet@<compiled>>=============== ==catch table | catch type: break st: 0010 ed: 0014 sp: 0000 cont: 0014 |------------------------------------------------------------------------ local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1) [ 2] str 0000 trace 8 ( 1) 0002 trace 1 ( 2) 0004 putstring "hello" 0006 setlocal_OP__WC__0 2 0008 trace 1 ( 3) 0010 putobject 3 0012 send <callinfo!mid:times, argc:0, block:block in greet> 0014 trace 16 ( 6) 0016 leave ( 3) == disasm: <RubyVM::InstructionSequence:block in greet@<compiled>>====== == catch table | catch type: redo st: 0002 ed: 0009 sp: 0000 cont: 0002 | catch type: next st: 0002 ed: 0009 sp: 0000 cont: 0009 |------------------------------------------------------------------------ 0000 trace 256 ( 3) 0002 trace 1 ( 4) 0004 putself 0005 getlocal_OP__WC__1 2 0007 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP> 0009 trace 512 ( 5) 0011 leave ( 4) def greet str = “hello” 3.times do puts str end end greet
  • 79.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self def greet str = “hello” 3.times do puts str end end greet
  • 80.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave str svar/cref ep def greet str = “hello” 3.times do puts str end end greet
  • 81.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] str svar/cref “hello” ep 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave def greet str = “hello” 3.times do puts str end end greet
  • 82.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] str svar/cref ep “hello” 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave def greet str = “hello” 3.times do puts str end end greet
  • 83.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp self type [TOP LEVEL] :greet self special rb_control_frame_t pc sp self type [METHOD] str svar/cref ep 3 “hello” 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave def greet str = “hello” 3.times do puts str end end greet
  • 84.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp [TOP LEVEL] :greet self special rb_control_frame_t pc sp [METHOD] 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave str svar/cref ep 3 “hello” rb_block_t iseq ep … 0004 putself 0005 getlocal 2, 1 0007 opt_send_simple puts 0011 leave def greet str = “hello” 3.times do puts str end end greet
  • 85.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp [TOP LEVEL] :greet self special rb_control_frame_t pc sp [METHOD] 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave str svar/cref ep 3 “hello” svar/cref special rb_block_t iseq ep … rb_control_frame_t pc sp [METHOD] ep times… 0004 putself 0005 getlocal 2, 1 0007 opt_send_simple puts 0011 leave def greet str = “hello” 3.times do puts str end end greet
  • 86.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp [TOP LEVEL] :greet self special rb_control_frame_t pc sp [METHOD] 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave str svar/cref ep 3 “hello” svar/cref special svar/cref rb_block_t iseq ep … rb_control_frame_t pc sp [METHOD] ep times… rb_con… pc sp [BLOCK] ep special 0004 putself 0005 getlocal 2, 1 0007 opt_send_simple puts 0011 leave def greet str = “hello” 3.times do puts str end end greet
  • 87.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp [TOP LEVEL] :greet self special rb_control_frame_t pc sp [METHOD] 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave str svar/cref ep 3 “hello” svar/cref special svar/cref rb_block_t iseq ep … rb_control_frame_t pc sp [METHOD] ep times… rb_con… pc sp [BLOCK] ep special self 0004 putself 0005 getlocal 2, 1 0007 opt_send_simple puts 0011 leave def greet str = “hello” 3.times do puts str end end greet
  • 88.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp [TOP LEVEL] :greet self special rb_control_frame_t pc sp [METHOD] 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave str svar/cref ep 3 “hello” svar/cref special svar/cref rb_block_t iseq ep … rb_control_frame_t pc sp [METHOD] ep times… rb_con… pc sp [BLOCK] ep 0004 putself 0005 getlocal 2, 1 0007 opt_send_simple puts 0011 leave special self “hello” def greet str = “hello” 3.times do puts str end end greet
  • 89.
    0006 putobject :greet 0008putiseq greet 0010 opt_send_simple define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 leave rb_control_frame_t pc sp [TOP LEVEL] :greet self special rb_control_frame_t pc sp [METHOD] 0004 putstring "hello" 0006 setlocal 2, 0 0010 putobject 3 0012 send times 0016 leave str svar/cref ep 3 “hello” svar/cref special svar/cref rb_block_t iseq ep … rb_control_frame_t pc sp [METHOD] ep times… rb_con… pc sp [BLOCK] ep 0004 putself 0005 getlocal 2, 1 0007 opt_send_simple puts 0011 leave special self $ “hello” def greet str = “hello” 3.times do puts str end end greet
  • 90.
  • 91.
    And what ifthat scope doesn’t exist anymore?
  • 92.
    The heap and rb_proc_tto the rescue! o/
  • 93.
  • 94.
    == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>========== localtable (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1) [ 2] f 0002 putspecialobject 1 0004 putspecialobject 2 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple <callinfo!mid:core#define_method, argc:3, ARGS_SKIP> 0012 pop 0015 putself 0016 opt_send_simple <callinfo!mid:greet, argc:0, FCALL|VCALL|ARGS_SKIP> 0018 setlocal_OP__WC__0 2 0022 getlocal_OP__WC__0 2 0024 opt_send_simple <callinfo!mid:call, argc:0, ARGS_SKIP> 0026 leave == disasm: <RubyVM::InstructionSequence:greet@<compiled>>=============== == catch table | catch type: break st: 0010 ed: 0013 sp: 0000 cont: 0013 |------------------------------------------------------------------------ local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1) [ 2] str 0004 putstring "hello" 0006 setlocal_OP__WC__0 2 0010 putself 0011 send <callinfo!mid:lambda, argc:0, block:block in greet, FCALL> 0015 leave ( 3) == disasm: <RubyVM::InstructionSequence:block in greet@<compiled>>====== == catch table | catch type: redo st: 0002 ed: 0009 sp: 0000 cont: 0002 | catch type: next st: 0002 ed: 0009 sp: 0000 cont: 0009 |------------------------------------------------------------------------ 0004 putself 0005 getlocal_OP__WC__1 2 0007 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP> 0011 leave def greet str = “hello” lambda do puts str end end f = greet f.call
  • 95.
    def greet str =“hello” lambda do puts str end end f = greet f.call [ 2] f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self
  • 96.
    [ 2] f 0006putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call rb_control_frame_t pc sp [TOP LEVEL] ep rb_control_frame_t pc sp [METHOD] ep f svar/cref special :greet self str svar/cref special
  • 97.
    [ 2] f 0006putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call rb_control_frame_t pc sp [TOP LEVEL] ep rb_control_frame_t pc sp [METHOD] ep f svar/cref special :greet self str svar/cref special “hello”
  • 98.
    [ 2] f 0006putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello” rb_control_frame_t pc sp [TOP LEVEL] ep rb_control_frame_t pc sp [METHOD] ep f svar/cref special :greet self str svar/cref special
  • 99.
    [ 2] f 0006putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello” rb_control_frame_t pc sp [TOP LEVEL] ep rb_control_frame_t pc sp [METHOD] ep f svar/cref special :greet self str svar/cref special self
  • 100.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self str rb_control_frame_t pc sp [METHOD] epsvar/cref special self lambda [ 2]f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello” str svar/cref special self “hello”
  • 101.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self str rb_control_frame_t pc sp [METHOD] epsvar/cref special self lambda [ 2]f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello” str svar/cref special self “hello”
  • 102.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self str rb_control_frame_t pc sp [METHOD] epsvar/cref special self lambda rb_proc_t rb_block_t EP [ 2]f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello” str svar/cref special self “hello”
  • 103.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self str rb_control_frame_t pc sp [METHOD] epsvar/cref special self lambda rb_proc_t rb_block_t EP [ 2]f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello” str svar/cref special self “hello”
  • 104.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self str rb_control_frame_t pc sp [METHOD] epsvar/cref special self lambda rb_proc_t rb_block_t EP [ 2]f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave 0004 putstring "hello" 0006 setlocal 2, 0 0010 putself 0011 send lambda 0015 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello” str svar/cref special self “hello”
  • 105.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self lambda str svar/cref special selfrb_proc_t rb_block_t EP [ 2]f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello”
  • 106.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self str svar/cref special selfrb_proc_t rb_block_t EP [ 2]f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello”
  • 107.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self lambda str svar/cref special selfrb_proc_t rb_block_t EP [ 2]f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello”
  • 108.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self lambda str svar/cref special selfrb_proc_t rb_block_t EP svar/cref rb_control_frame_t pc sp [BLOCK] ep 0004 putself 0005getlocal 2, 1 0007 opt_send_simple puts 0011 leave special [ 2] f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello”
  • 109.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self lambda str svar/cref special selfrb_proc_t rb_block_t EP svar/cref rb_control_frame_t pc sp [BLOCK] ep special self 0004 putself 0005getlocal 2, 1 0007 opt_send_simple puts 0011 leave [ 2] f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello”
  • 110.
    rb_control_frame_t pc sp [TOP LEVEL] epf svar/cref special :greet self lambda str svar/cref special selfrb_proc_t rb_block_t EP svar/cref rb_control_frame_t pc sp [BLOCK] ep special self “hello” 0004 putself 0005getlocal 2, 1 0007 opt_send_simple puts 0011 leave [ 2] f 0006 putobject :greet 0008 putiseq greet 0010 opt_send_simple core#define_method 0012 pop 0015 putself 0016 opt_send_simple greet 0018 setlocal 2, 0 0022 getlocal 2, 0 0024 opt_send_simple call 0026 leave def greet str = “hello” lambda do puts str end end f = greet f.call “hello”
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.