RubyVM::AST::Node → String of Code
● RubyVM::AST::Node#to_source
○ It defined by AstExt (refinements patch)
Slide 19
Slide 19 text
RubyVM::AST::Node → String of Code
● Use File.binread
path = m.source_location[0]
range = node.first_index..node.last_index
source = File.binread(path)[range]
# => “def foo() bar() end”
note: first|last_index are defined here
https://github.com/pocke/rinline/blob/2cb6ffb055c619998973f5bd2a349fcb6599e95c/lib/rinline/ext/ast_ext.rb#L144-L161
Slide 20
Slide 20 text
String of code → Inline expanded code
● Replacing method call with called method body
● I will talk it on the next section!
Slide 21
Slide 21 text
Inline expanded code → Ruby program by eval
● Use class_eval
Object.class_eval(inline_expanded_code)
Slide 22
Slide 22 text
Replacing method
call
Slide 23
Slide 23 text
Method call in Ruby
● FCALL → method call with arguments
■ foo(1, 2)
● VCALL → method call without arguments
■ foo
● CALL → method call with receiver
■ x.foo
● OPCALL → method call with operator
■ foo + bar
Slide 24
Slide 24 text
Rinline supports FCALL and VCALL
● Easy to support (Supporting CALL is difficult)
○ CALL has a receiver, and receiver klass is
unknown statically in most cases
Slide 25
Slide 25 text
How to replace
● RubyVM::AST::Node → … → Replaced code
Slide 26
Slide 26 text
How to replace
● RubyVM::AST::Node → VCALL/FCALL Node →
UnboundMethod → RubyVM::AST::Node → String of
called method → lvar renamed code → Replaced
code
Slide 27
Slide 27 text
Example
def foo
x = 1
bar
p x * 3
end
def bar
x = 21
p x * 2
end
RubyVM::AST::Node → String of called method
def_src = method_def_node.to_source
Slide 33
Slide 33 text
String of called method → lvar renamed code
● Before embed, renaming lvar is needed
Slide 34
Slide 34 text
Example
def foo
x = 1
bar
p x * 3 # => 3
end
def bar
x = 21
p x * 2 # => 42
end
Slide 35
Slide 35 text
Example: It will be break!
def foo
x = 1
x = 21
p x * 2 # => 42
p x * 3 # => 63
end
def bar
x = 21
p x * 2
end
Slide 36
Slide 36 text
Fix local var collision
def foo
x = 1
x_XXX = 21
p x_XXX * 2 # => 42
p x * 3 # => 3
end
def bar
x = 21
p x * 2
end
Slide 37
Slide 37 text
lvar renamed code → Replaced code
def foo
x = 1
x_XXX = 21
p x_XXX * 2
p x * 3
end
It is ok, but actually the
replaced code will be
enclosed with double
braces
Slide 38
Slide 38 text
lvar renamed code → Replaced code
def foo
x = 1
((x_XXX = 21
p x_XXX * 2))
p x * 3
end
Slide 39
Slide 39 text
Why braces
bar * 3 # => 9 def bar
1 + 2
end
Slide 40
Slide 40 text
Why braces
bar * 3 # => 9
1 + 2 * 3 # => 7
(1 + 2) * 3 # => 9
def bar
1 + 2
end
Slide 41
Slide 41 text
Why double braces
p bar def bar
1; 2
end
Slide 42
Slide 42 text
Why double braces
p (1; 2)
# => SyntaxError!
p ((1; 2))
# => Valid
def bar
1; 2
end
Slide 43
Slide 43 text
The future of Rinline
Slide 44
Slide 44 text
Meaningful optimization
● Currently it makes micro benchmark faster
● But it does not make actual app faster
○ For example, I tried to optimize optcarrot, but it
does not change the FPS
Slide 45
Slide 45 text
Fix many bugs
● Rinline generates invalid code
○ For example: It breaks on define_method
Slide 46
Slide 46 text
Support super
● Currently, Rinline just ignores `super` node
● It can support `super` with
UnboundMethod#super_method
○ https://twitter.com/udzura/status/11303947244
58565633
Slide 47
Slide 47 text
Conclusion
Slide 48
Slide 48 text
Conclusion
● RubyVM::AST.of is useful for “Dark Power”
● Rinline is a PoC of inline expansion for Ruby
Slide 49
Slide 49 text
pp self
● Masataka “pocke” Kuwabara
● Work for Bit Journey, Inc.
Thank you for listening!