Upgrade to Pro — share decks privately, control downloads, hide ads and more …

railsdm2019.pdf

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

 railsdm2019.pdf

Avatar for Shuichi Tamayose

Shuichi Tamayose

March 22, 2019
Tweet

More Decks by Shuichi Tamayose

Other Decks in Programming

Transcript

  1. RAILS DEVELOPERS MEETUP 2019 SESSION: C - 10 2019/03/22 (ۚ)

    ۄد मҰ Ruby ͷίʔυղੳͷʮ੩ʯͱʮಈʯ
  2. ࣈ۟ղੳ 1 require "ripper" 2 3 pp Ripper.lex("puts 2 +

    2") Ruby ͷඪ४ϥΠϒϥϦʹ͋Δ Ripper.lex Λ࢖͏͜ͱͰτʔΫϯ৘ใΛऔಘͰ͖Δ ਤ2. τʔΫϯྻΛಘΔͨΊͷίʔυ ߏจղੳͷલॲཧɺRuby ͷιʔεΛಡΈࠐΜͰτʔΫϯʹม׵͢Δૢ࡞
  3. ࣈ۟ղੳ [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4], :on_sp, "

    ", EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]] 1 require "ripper" 2 3 pp Ripper.lex("puts 2 + 2") ʮҐஔ৘ใʯʮτʔΫϯͷछผʯʮτʔΫϯͷจࣈྻʯʮࣈ۟ղੳͷঢ়ଶʯ͕ಘΒΕΔ ਤ3. ಘΒΕͨτʔΫϯ৘ใ
  4. ࣈ۟ղੳ [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4], :on_sp, "

    ", EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]] 1 require "ripper" 2 3 pp Ripper.lex("puts 2 + 2") ʮҐஔ৘ใʯʮτʔΫϯͷछผʯʮτʔΫϯͷจࣈྻʯʮࣈ۟ղੳͷঢ়ଶʯ͕ಘΒΕΔ ਤ3. ಘΒΕͨτʔΫϯ৘ใ
  5. ࣈ۟ղੳ [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4], :on_sp, "

    ", EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]] ʮҐஔ৘ใʯʮτʔΫϯͷछผʯʮτʔΫϯͷจࣈྻʯʮࣈ۟ղੳͷঢ়ଶʯ͕ಘΒΕΔ ਤ3. ಘΒΕͨτʔΫϯ৘ใ 1 require "ripper" 2 3 pp Ripper.lex("puts 2 + 2")
  6. ࣈ۟ղੳ [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4], :on_sp, "

    ", EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]] ʮҐஔ৘ใʯʮτʔΫϯͷछผʯʮτʔΫϯͷจࣈྻʯʮࣈ۟ղੳͷঢ়ଶʯ͕ಘΒΕΔ ਤ3. ಘΒΕͨτʔΫϯ৘ใ 1 require "ripper" 2 3 pp Ripper.lex("puts 2 + 2")
  7. ࣈ۟ղੳ [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4], :on_sp, "

    ", EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]] ʮҐஔ৘ใʯʮτʔΫϯͷछผʯʮτʔΫϯͷจࣈྻʯʮࣈ۟ղੳͷঢ়ଶʯ͕ಘΒΕΔ ਤ3. ಘΒΕͨτʔΫϯ৘ใ 1 require "ripper" 2 3 pp Ripper.lex("puts 2 + 2")
  8. ࣈ۟ղੳ τʔΫϯͷछྨ͸༷ʑ :CHAR :__end__ :backref :backtick :comma :comment :const :cvar

    :embdoc :embdoc_beg :embdoc_end :embexpr_beg :embexpr_end :embvar :float :gvar :heredoc_beg :heredoc_end :ident :ignored_nl :imaginary :int :ivar :kw :label :label_end :lbrace :lbracket :lparen :nl :op :period :qsymbols_beg :qwords_beg :rational :rbrace :rbracket :regexp_beg :regexp_end :rparen :semicolon :sp :symbeg :symbols_beg :tlambda :tlambeg :tstring_beg :tstring_content :tstring_end :words_beg :words_sep ਤ4. Ripper::SCANNER_EVENTS ͰಘΒΕΔτʔΫϯͷछྨ
  9. ࣈ۟ղੳ τʔΫϯྻͷঢ়ଶͰ͸ಘΒΕΔ৘ใ͕গͳ͍ͷͰ࣮ࡍͷղੳͰ͸͜ͷޙʹ ग़ͯ͘Δߏจ໦͕࢖༻͞ΕΔ͜ͱ͕ଟ͍ [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4],

    :on_sp, " ", EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]]
  10. ߏจղੳ (SCOPE@1:0-1:10 tbl: [] args: nil body: (FCALL@1:0-1:10 :puts (ARRAY@1:5-1:10

    (OPCALL@1:5-1:10 (LIT@1:5-1:6 2) :+ (ARRAY@1:9-1:10 (LIT@1:9-1:10 2) nil)) nil))) ߏจ໦ΛಘΔͨΊͷίʔυ pp RubyVM::AbstractSyntaxTree.parse("puts 2 + 2")
  11. (SCOPE@1:0-1:10 tbl: [] args: nil body: (FCALL@1:0-1:10 :puts (ARRAY@1:5-1:10 (OPCALL@1:5-1:10

    (LIT@1:5-1:6 2) :+ (ARRAY@1:9-1:10 (LIT@1:9-1:10 2) nil)) nil))) ͜ͷ··ͩͱগ͠ݟͮΒ͍ͷͰ؆қతʹදࣔ SCOPE table: [ none ] args: [ none ] FCALL method_id: puts OPCALL
 :+ LIT 2 LIT 2
  12. [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4], :on_sp, " ",

    EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]] SCOPE table: [ none ] args: [ none ] FCALL method_id: puts OPCALL
 :+ LIT 2 LIT 2
  13. SCOPE table: [ none ] args: [ none ] FCALL

    method_id: puts OPCALL
 :+ LIT 2 LIT 2 puts ͕ϝιου໊ͱ͍͏͜ͱ͕Θ͔Δ [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4], :on_sp, " ", EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]]
  14. SCOPE table: [ none ] args: [ none ] FCALL

    method_id: puts OPCALL
 :+ LIT 2 LIT 2 puts ͷҾ਺ͱͯ͠ 2 + 2 ͕ݺͼग़͞Ε͍ͯΔ [[[1, 0], :on_ident, "puts", EXPR_CMDARG], [[1, 4], :on_sp, " ", EXPR_CMDARG], [[1, 5], :on_int, "2", EXPR_END], [[1, 6], :on_sp, " ", EXPR_END], [[1, 7], :on_op, "+", EXPR_BEG], [[1, 8], :on_sp, " ", EXPR_BEG], [[1, 9], :on_int, "2", EXPR_END], [[1, 10], :on_nl, "\n", EXPR_BEG]]
  15. ߏจղੳ RubyVM::AbstractSyntaxTree::Node#type ͰಘΒΕΔϊʔυͷछྨ (ࣗ෼ௐ΂) :SCOPE :BLOCK :IF :UNLESS :CASE :CASE2

    :WHEN :WHILE :UNTIL :ITER :FOR :FOR_MASGN :BREAK :NEXT :REDO :RETRY :BEGIN :RESCUE :RESBODY :ENSURE :AND :OR :MASGN :LASGN :DASGN :DASGN_CURR :GASGN :IASGN :CDECL :CVASGN :OP_ASGN1 :OP_ASGN2 :OP_ASGN_AND :OP_ASGN_OR :OP_CDECL :CALL :OPCALL :FCALL :VCALL :QCALL :SUPER :ZSUPER :ARRAY :ZARRAY :VALUES :HASH :RETURN :YIELD :LVAR :DVAR :GVAR :IVAR :CONST :CVAR :NTH_REF :BACK_REF :MATCH :MATCH2 :MATCH3 :LIT :STR :DSTR :XSTR :DXSTR :EVSTR :DREGX :ONCE :ARGS :OPT_ARG :KW_ARG :POSTARG :METHREF :ARGSCAT :ARGSPUSH :SPLAT :BLOCK_PASS :DEFN :DEFS :ALIAS :VALIAS :UNDEF :CLASS :MODULE :SCLASS :COLON2 :COLON3 :DOT2 :DOT3 :FLIP2 :FLIP3 :SELF :NIL :TRUE :FALSE :ERRINFO :DEFINED :POSTEXE :DSYM :ATTRASGN :LAMBDA
  16. ฤूૢ࡞ (ϊʔυͷஔ׵) OPCALL
 :+ LIT 1 LIT 1 1 +

    1 Λද͢ߏจ໦ OPCALL
 :+ STR "foo" STR "bar" "foo" + "bar" Λද͢ߏจ໦ ࠨͷ໦ߏ଄͔Βӈͷ໦ߏ଄ʹมܗ͍ͨ͠ ɾҰ൪࠷୹ͷखॱ͸ LIT ϊʔυΛ STR ϊʔυʹஔ͖׵͑Δ͜ͱ
  17. ฤूૢ࡞ (ϊʔυͷஔ׵) OPCALL
 :+ STR "foo" STR "bar" OPCALL
 :+

    STR "foo" STR "bar" ɾҰ൪࠷୹ͷखॱ͸ LIT ϊʔυΛ STR ϊʔυʹஔ͖׵͑Δ͜ͱ LIT ϊʔυΛ STR ϊʔυʹஔ׵͢Δ ࠨͷ໦ߏ଄͔Βӈͷ໦ߏ଄ʹมܗ͍ͨ͠
  18. ฤूૢ࡞ (ϊʔυͷஔ׵) OPCALL
 :+ STR "foo" STR "bar" OPCALL
 :+

    STR "foo" STR "bar" ஔ׵ૢ࡞ʹΑͬͯ 2ͭͷϊʔυ͕Ұக͢Δ ɾࠓճͷ࣮૷Ͱ͸ϊʔυͷछผ͚ͩΛஔ׵ͷର৅ͱͨ͠
  19. ast_distance gem 2 ͭͷ RubyVM::AbstractSyntaxTree::Node ͷฤूڑ཭Λܭࢉ͢Δ gem ɾhttps://github.com/siman-man/ast_distance 1 require

    "ast_distance" 2 3 ast1 = RubyVM::AbstractSyntaxTree.parse("1 + 1") 4 ast2 = RubyVM::AbstractSyntaxTree.parse("'hello' + 'world'") 5 6 puts ASTDistance.tree_edit_distance(ast1, ast2) #=> 2 ɾฤूڑ཭ͷ஋͕খ͍͞΄ͲࣅͨΑ͏ͳίʔυʹͳ͍ͬͯΔՄೳੑ͕ߴ͍
  20. ast_distance gem 2 ͭͷ RubyVM::AbstractSyntaxTree::Node ͷฤूڑ཭Λܭࢉ͢Δ gem ɾhttps://github.com/siman-man/ast_distance 1 require

    "ast_distance" 2 3 ast1 = RubyVM::AbstractSyntaxTree.parse("1 + 1") 4 ast2 = RubyVM::AbstractSyntaxTree.parse("2 + 2") 5 6 puts ASTDistance.tree_edit_distance(ast1, ast2) #=> 0 ɾฤूڑ཭ͷ஋͕খ͍͞΄ͲࣅͨΑ͏ͳίʔυʹͳ͍ͬͯΔՄೳੑ͕ߴ͍ ฤूڑ཭͕ 0 ͩͱίʔυ͕͔ͳΓྨࣅ͍ͯ͠Δ
  21. flay gem ߏจ໦ʹରͯ͠ϋογϡ஋Λܭࢉۙ͠ࣅ͍ͯ͠ΔίʔυΛݕग़͢Δ ɾhttps://github.com/seattlerb/flay ɾͪ͜ΒͰ͸ߏจղੳʹ ruby_parser ͕࢖༻͞Ε͍ͯΔ 1 def add(a,

    b) 2 a + b 3 end 4 5 def sub(a, b) 6 a - b 7 end $ flay -v --diff --mass=5 example.rb Processing example.rb Total score (lower is better) = 10 1) Similar code found in :defn (mass = 10) A: example.rb:1 B: example.rb:5 A: def add(a, b) B: def sub(a, b) A: (a + b) B: (a - b) end example.rb
  22. Ruby ιʔεϑΝΠϧؒͷґଘؔ܎ͷ୳ࡧ Ruby ͷϑΝΠϧؒͷґଘؔ܎Λௐ΂ΔॲཧΛॻ͍ͯΈΔ 1 require "prime" 2 3 pp

    2.prime? ɾྫ͑͹ԼͷྫͰ͸ example.rb ͸ prime.rb ʹґଘ͍ͯ͠ΔͱΈͳ͢ example.rb ґଘؔ܎ͷ൑ఆʹ͸ require ΍ load ౳ͷଞͷ Ruby ίʔυͷ ಡΈࠐΈॲཧΛར༻͢Δ
  23. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ํ਑ ಡΈࠐΈॲཧΛݟ͚ͭΔͨΊʹ FCALL ϊʔυΛར༻͢Δ ɾFCALL ϊʔυ͸ϨγʔόΛࢦఆ͠ͳ͍ϝιουݺͼग़͠ <FCALL> ├───── :require (method

    id) └───── <ARRAY> ├───── <STR> │ └───── "prime" (value) └───── nil (unknown) require "prime"
  24. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3

    next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end શͯͷ RubyVM::AbstractSyntaxTree::Node ʹରͯ͠ॲཧΛߦ͑ΔΑ͏ͳ ϝιουΛఆٛ͢Δ
  25. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3

    next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end શͯͷ RubyVM::AbstractSyntaxTree::Node ʹରͯ͠ॲཧΛߦ͑ΔΑ͏ͳ ϝιουΛఆٛ͢Δ RubyVM::AbstractSyntaxTree::Node#children ϝιουͰࢠϊʔυ৘ใΛऔಘ ࢠϊʔυ৘ใ͸഑ྻͰฦͬͯ͘Δ
  26. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3

    next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end શͯͷ RubyVM::AbstractSyntaxTree::Node ʹରͯ͠ॲཧΛߦ͑ΔΑ͏ͳ ϝιουΛఆٛ͢Δ Node ͷΠϯελϯεͰ͸ແ͍৔߹͸ॲཧΛεΩοϓ
  27. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ <FCALL> ├───── :require (method id) └───── <ARRAY> ├───── <STR>

    │ └───── "prime" (value) └───── nil (unknown) ࢠϊʔυ͕ RubyVM::AbstractSyntaxTree::Node ͱ͸ݶΒͳ͍ FCALL ͷࢠཁૉ
  28. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ <FCALL> ├───── :require (method id) └───── <ARRAY> ├───── <STR>

    │ └───── "prime" (value) └───── nil (unknown) FCALL ͷࢠཁૉ ࢠϊʔυ͕ RubyVM::AbstractSyntaxTree::Node ͱ͸ݶΒͳ͍ Symbol ΦϒδΣΫτ
  29. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ <FCALL> ├───── :require (method id) └───── <ARRAY> ├───── <STR>

    │ └───── "prime" (value) └───── nil (unknown) FCALL ͷࢠཁૉ ࢠϊʔυ͕ RubyVM::AbstractSyntaxTree::Node ͱ͸ݶΒͳ͍ Node ΦϒδΣΫτ
  30. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3

    next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end શͯͷ RubyVM::AbstractSyntaxTree::Node ʹରͯ͠ॲཧΛߦ͑ΔΑ͏ͳ ϝιουΛఆٛ͢Δ Node ʹର͢Δॲཧͷݺͼग़͠
  31. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3

    next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end શͯͷ RubyVM::AbstractSyntaxTree::Node ʹରͯ͠ॲཧΛߦ͑ΔΑ͏ͳ ϝιουΛఆٛ͢Δ શͯͷϊʔυΛ୳ࡧ͢ΔͨΊʹ࠶ؼతʹ ॲཧΛݺͼग़͢
  32. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3

    next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end શͯͷ RubyVM::AbstractSyntaxTree::Node ʹରͯ͠ॲཧΛߦ͑ΔΑ͏ͳ ϝιουΛఆٛ͢Δ
  33. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def get_feature(node:) 2 case node.children[0] 3 when :require,

    :require_relative, :load 4 node.children[1].children[0].children[0] 5 when :autoload 6 node.children[1].children[1].children[0] 7 end 8 end ϊʔυͷ৘ใ͔ΒͲͷ Ruby ϑΝΠϧΛಡΈࠐ΋͏ͱ͍ͯ͠Δͷ͔Λऔಘ͢Δ
  34. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def get_feature(node:) 2 case node.children[0] 3 when :require,

    :require_relative, :load 4 node.children[1].children[0].children[0] 5 when :autoload 6 node.children[1].children[1].children[0] 7 end 8 end ϊʔυͷ৘ใ͔ΒͲͷ Ruby ϑΝΠϧΛಡΈࠐ΋͏ͱ͍ͯ͠Δͷ͔Λऔಘ͢Δ ࢠϊʔυΛͨͲͬͯ஋Λऔಘ͢Δ
  35. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ <FCALL> ├───── :require (method id) └───── <ARRAY> ├───── <STR>

    │ └───── "prime" (value) └───── nil (unknown) node.children[1].children[0].children[0] ϊʔυͷ৘ใ͔ΒͲͷ Ruby ϑΝΠϧΛಡΈࠐ΋͏ͱ͍ͯ͠Δͷ͔Λऔಘ͢Δ
  36. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ <FCALL> ├───── :require (method id) └───── <ARRAY> ├───── <STR>

    │ └───── "prime" (value) └───── nil (unknown) ϊʔυͷ৘ใ͔ΒͲͷ Ruby ϑΝΠϧΛಡΈࠐ΋͏ͱ͍ͯ͠Δͷ͔Λऔಘ͢Δ node.children[1].children[0].children[0]
  37. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ ϊʔυͷ৘ใ͔ΒͲͷ Ruby ϑΝΠϧΛಡΈࠐ΋͏ͱ͍ͯ͠Δͷ͔Λऔಘ͢Δ <FCALL> ├───── :require (method id) └─────

    <ARRAY> ├───── <STR> │ └───── "prime" (value) └───── nil (unknown) node.children[1].children[0].children[0]
  38. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ <FCALL> ├───── :require (method id) └───── <ARRAY> ├───── <STR>

    │ └───── "prime" (value) └───── nil (unknown) node.children[1].children[0].children[0] ϊʔυͷ৘ใ͔ΒͲͷ Ruby ϑΝΠϧΛಡΈࠐ΋͏ͱ͍ͯ͠Δͷ͔Λऔಘ͢Δ ໨తͷ஋ʹ౸ୡ
  39. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def get_feature(node:) 2 case node.children[0] 3 when :require,

    :require_relative, :load 4 node.children[1].children[0].children[0] 5 when :autoload 6 node.children[1].children[1].children[0] 7 end 8 end ϊʔυͷ৘ใ͔ΒͲͷ Ruby ϑΝΠϧΛಡΈࠐ΋͏ͱ͍ͯ͠Δͷ͔Λऔಘ͢Δ
  40. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 LOAD_METHODS = %i(require require_relative load autoload) 2 3

    def search_load_features(ast:) 4 features = [] 5 6 inspector(ast: ast) do |node| 7 next if node.type != :FCALL 8 next unless LOAD_METHODS.include?(node.children[0]) 9 10 features << get_feature(node: node) 11 end 12 13 features 14 end AST ৘ใ͔Βϩʔυ͍ͯ͠ΔϥΠϒϥϦͷҰཡΛऔಘ͢Δ
  41. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 LOAD_METHODS = %i(require require_relative load autoload) 2 3

    def search_load_features(ast:) 4 features = [] 5 6 inspector(ast: ast) do |node| 7 next if node.type != :FCALL 8 next unless LOAD_METHODS.include?(node.children[0]) 9 10 features << get_feature(node: node) 11 end 12 13 features 14 end AST ৘ใ͔Βϩʔυ͍ͯ͠ΔϥΠϒϥϦͷҰཡΛऔಘ͢Δ inspector Λ࢖༻ͯ͠ϊʔυશମʹରͯ͠ॲཧΛߦ͏
  42. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 LOAD_METHODS = %i(require require_relative load autoload) 2 3

    def search_load_features(ast:) 4 features = [] 5 6 inspector(ast: ast) do |node| 7 next if node.type != :FCALL 8 next unless LOAD_METHODS.include?(node.children[0]) 9 10 features << get_feature(node: node) 11 end 12 13 features 14 end 2 ͭͷϑΟϧλϦϯάॲཧΛೖΕ͍ͯΔ ɾFCALL ϊʔυ͔Ͳ͏͔ͷ൑ఆ ɾϥΠϒϥϦΛϩʔυ͢Δϝιου͔Ͳ͏͔ͷ൑ఆ AST ৘ใ͔Βϩʔυ͍ͯ͠ΔϥΠϒϥϦͷҰཡΛऔಘ͢Δ
  43. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 LOAD_METHODS = %i(require require_relative load autoload) 2 3

    def search_load_features(ast:) 4 features = [] 5 6 inspector(ast: ast) do |node| 7 next if node.type != :FCALL 8 next unless LOAD_METHODS.include?(node.children[0]) 9 10 features << get_feature(node: node) 11 end 12 13 features 14 end ϥΠϒϥϦ໊Λऔಘͯ͠Ϧετʹ௥Ճ AST ৘ใ͔Βϩʔυ͍ͯ͠ΔϥΠϒϥϦͷҰཡΛऔಘ͢Δ
  44. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 LOAD_METHODS = %i(require require_relative load autoload) 2 3

    def search_load_features(ast:) 4 features = [] 5 6 inspector(ast: ast) do |node| 7 next if node.type != :FCALL 8 next unless LOAD_METHODS.include?(node.children[0]) 9 10 features << get_feature(node: node) 11 end 12 13 features 14 end AST ৘ใ͔Βϩʔυ͍ͯ͠ΔϥΠϒϥϦͷҰཡΛऔಘ͢Δ औಘͨ͠ϥΠϒϥϦ໊ͷҰཡΛฦ͢
  45. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def find_path(feature) 2 RubyVM.resolve_feature_path(feature).last 3 rescue LoadError 4

    nil 5 end ϥΠϒϥϦ໊͔ΒϑΝΠϧύεΛऔಘ͢Δ ϥΠϒϥϦ໊͔ΒϑΝΠϧύεΛऔಘ
  46. RubyVM.resolve_feature_path ɾRuby 2.6 Ͱ௥Ճ͞Εͨػೳ require load ϑΝΠϧύεऔಘ ISeq ɾrequire ౳Ͱଞͷ

    Ruby ϓϩάϥϜΛಡΈࠐΉࡍʹͲͷϑΝΠϧ͕ ಡΈࠐ·ΕΔͷ͔Λௐ΂Δ͜ͱ͕ग़དྷΔ ϑΝΠϧύεݕࡧ ίϯύΠϧ ਤ4. Ruby ͷଞͷϓϩάϥϜͷಡΈࠐΈॲཧ
  47. RubyVM.resolve_feature_path require load ϑΝΠϧύεऔಘ ISeq ϑΝΠϧύεݕࡧ ίϯύΠϧ ɾRuby 2.6 Ͱ௥Ճ͞Εͨػೳ

    ɾrequire ౳Ͱଞͷ Ruby ϓϩάϥϜΛಡΈࠐΉࡍʹͲͷϑΝΠϧ͕ ಡΈࠐ·ΕΔͷ͔Λௐ΂Δ͜ͱ͕ग़དྷΔ ਤ4. Ruby ͷଞͷϓϩάϥϜͷಡΈࠐΈॲཧ resolve_feature_path ͸͜͜·ͰΛ΍Δ
  48. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ
  49. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ parse_file Ͱ͸Ҿ਺ʹ AST Λऔಘ͍ͨ͠ϑΝΠϧύεΛࢦఆ͢Δ
  50. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ AST ৘ใ͔ΒಡΈࠐΜͰ͍ΔϥΠϒϥϦͷҰཡΛऔಘͯ͘͠Δ
  51. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ ֤ϥΠϒϥϦຖʹॲཧΛߦ͏
  52. 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash, key|

    hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ ϑΝΠϧͷґଘؔ܎Λௐ΂Δ ࠷ॳ͸ϥΠϒϥϦ໊Ͱ୳ͯ͠ɺ΋͠ݟ͔ͭΒͳ͍৔߹͸૬ରύε ͕ࢦఆ͞ΕͨͱԾఆͯ͠ઈରύεʹม׵͔ͯ͠Β࠶౓୳ࡧ
  53. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ ϑΝΠϧύε͕ݟ͔ͭΒͳ͍৔߹͸ॲཧΛεΩοϓ
  54. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ ґଘؔ܎ͷ௥ՃΛߦ͏
  55. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ ґଘϑΝΠϧͷґଘؔ܎Λௐ΂ΔͨΊʹ࠶ؼతʹ୳ࡧΛߦ͏
  56. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷ 1 def check_file_dependencies(path:) 2 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 3 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 4 features = search_load_features(ast: ast) 5 6 features.each do |feature| 7 feature_path = find_path(feature) || 8 find_path(File.expand_path(feature, File.dirname(path))) 9 10 next if feature_path.nil? 11 12 file_dependencies[path] << feature_path 13 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 14 end 15 16 file_dependencies 17 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ ࠷ޙʹ݁ՌΛฦͯ͠ऴΘΓ
  57. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷·ͱΊ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3

    next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end શͯͷϊʔυʹରͯ͠ॲཧΛߦ͑ΔΑ͏ͳॲཧΛॻ͘ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3 next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end 10 11 def get_feature(node:) 12 case node.children[0] 13 when :require, :require_relative, :load 14 node.children[1].children[0].children[0] 15 when :autoload 16 node.children[1].children[1].children[0] 17 end 18 end 19 20 LOAD_METHODS = %i(require require_relative load autoload) 21 22 def search_load_features(ast:) 23 features = [] 24 25 inspect(ast: ast) do |node| 26 next if node.type != :FCALL 27 next unless LOAD_METHODS.include?(node.children[0]) 28 29 features << get_feature(node: node) 30 end 31 32 features 33 end 34 35 def find_path(feature) 36 RubyVM.resolve_feature_path(feature).last 37 rescue LoadError 38 nil 39 end 40 41 def check_file_dependencies(path:) 42 file_dependencies = Hash.new { |hash, key| hash[key] = [] } 43 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 44 features = search_load_features(ast: ast) 45 46 features.each do |feature| 47 feature_path = find_path(feature) || 48 find_path(File.expand_path(feature, File.dirname(path))) 49 50 next if feature_path.nil? 51 52 file_dependencies[path] << feature_path 53 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 54 end 55 56 file_dependencies 57 end check_file_dependencies.rb
  58. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷·ͱΊ 11 def get_feature(node:) 12 case node.children[0] 13 when :require,

    :require_relative, :load 14 node.children[1].children[0].children[0] 15 when :autoload 16 node.children[1].children[1].children[0] 17 end 18 end ϊʔυ৘ใ͔ΒಡΈࠐΜͰ͍ΔϥΠϒϥϦ໊Λऔಘ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3 next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end 10 11 def get_feature(node:) 12 case node.children[0] 13 when :require, :require_relative, :load 14 node.children[1].children[0].children[0] 15 when :autoload 16 node.children[1].children[1].children[0] 17 end 18 end 19 20 LOAD_METHODS = %i(require require_relative load autoload) 21 22 def search_load_features(ast:) 23 features = [] 24 25 inspect(ast: ast) do |node| 26 next if node.type != :FCALL 27 next unless LOAD_METHODS.include?(node.children[0]) 28 29 features << get_feature(node: node) 30 end 31 32 features 33 end 34 35 def find_path(feature) 36 RubyVM.resolve_feature_path(feature).last 37 rescue LoadError 38 nil 39 end 40 41 def check_file_dependencies(path:) 42 file_dependencies = Hash.new { |hash, key| hash[key] = [] } 43 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 44 features = search_load_features(ast: ast) 45 46 features.each do |feature| 47 feature_path = find_path(feature) || 48 find_path(File.expand_path(feature, File.dirname(path))) 49 50 next if feature_path.nil? 51 52 file_dependencies[path] << feature_path 53 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 54 end 55 56 file_dependencies 57 end check_file_dependencies.rb
  59. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷·ͱΊ 20 LOAD_METHODS = %i(require require_relative load autoload) 21 22

    def search_load_features(ast:) 23 features = [] 24 25 inspect(ast: ast) do |node| 26 next if node.type != :FCALL 27 next unless LOAD_METHODS.include?(node.children[0]) 28 29 features << get_feature(node: node) 30 end 31 32 features 33 end AST ৘ใ͔Βϩʔυ͍ͯ͠ΔϥΠϒϥϦͷҰཡΛऔಘ͢Δ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3 next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end 10 11 def get_feature(node:) 12 case node.children[0] 13 when :require, :require_relative, :load 14 node.children[1].children[0].children[0] 15 when :autoload 16 node.children[1].children[1].children[0] 17 end 18 end 19 20 LOAD_METHODS = %i(require require_relative load autoload) 21 22 def search_load_features(ast:) 23 features = [] 24 25 inspect(ast: ast) do |node| 26 next if node.type != :FCALL 27 next unless LOAD_METHODS.include?(node.children[0]) 28 29 features << get_feature(node: node) 30 end 31 32 features 33 end 34 35 def find_path(feature) 36 RubyVM.resolve_feature_path(feature).last 37 rescue LoadError 38 nil 39 end 40 41 def check_file_dependencies(path:) 42 file_dependencies = Hash.new { |hash, key| hash[key] = [] } 43 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 44 features = search_load_features(ast: ast) 45 46 features.each do |feature| 47 feature_path = find_path(feature) || 48 find_path(File.expand_path(feature, File.dirname(path))) 49 50 next if feature_path.nil? 51 52 file_dependencies[path] << feature_path 53 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 54 end 55 56 file_dependencies 57 end check_file_dependencies.rb
  60. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷·ͱΊ 35 def find_path(feature) 36 RubyVM.resolve_feature_path(feature).last 37 rescue LoadError 38

    nil 39 end ϥΠϒϥϦ໊͔ΒϑΝΠϧύεΛऔಘ͢Δ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3 next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end 10 11 def get_feature(node:) 12 case node.children[0] 13 when :require, :require_relative, :load 14 node.children[1].children[0].children[0] 15 when :autoload 16 node.children[1].children[1].children[0] 17 end 18 end 19 20 LOAD_METHODS = %i(require require_relative load autoload) 21 22 def search_load_features(ast:) 23 features = [] 24 25 inspect(ast: ast) do |node| 26 next if node.type != :FCALL 27 next unless LOAD_METHODS.include?(node.children[0]) 28 29 features << get_feature(node: node) 30 end 31 32 features 33 end 34 35 def find_path(feature) 36 RubyVM.resolve_feature_path(feature).last 37 rescue LoadError 38 nil 39 end 40 41 def check_file_dependencies(path:) 42 file_dependencies = Hash.new { |hash, key| hash[key] = [] } 43 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 44 features = search_load_features(ast: ast) 45 46 features.each do |feature| 47 feature_path = find_path(feature) || 48 find_path(File.expand_path(feature, File.dirname(path))) 49 50 next if feature_path.nil? 51 52 file_dependencies[path] << feature_path 53 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 54 end 55 56 file_dependencies 57 end check_file_dependencies.rb
  61. ಡΈࠐΈॲཧͷ୳ࡧ࣮૷·ͱΊ 41 def check_file_dependencies(path:) 42 file_dependencies = Hash.new { |hash,

    key| hash[key] = [] } 43 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 44 features = search_load_features(ast: ast) 45 46 features.each do |feature| 47 feature_path = find_path(feature) || 48 find_path(File.expand_path(feature, File.dirname(path))) 49 50 next if feature_path.nil? 51 52 file_dependencies[path] << feature_path 53 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 54 end 55 56 file_dependencies 57 end ϑΝΠϧͷґଘؔ܎Λௐ΂Δ 1 def inspector(ast:, &block) 2 ast.children.each do |child| 3 next unless child.instance_of?(RubyVM::AbstractSyntaxTree::Node) 4 5 yield child 6 7 inspector(ast: child, &block) 8 end 9 end 10 11 def get_feature(node:) 12 case node.children[0] 13 when :require, :require_relative, :load 14 node.children[1].children[0].children[0] 15 when :autoload 16 node.children[1].children[1].children[0] 17 end 18 end 19 20 LOAD_METHODS = %i(require require_relative load autoload) 21 22 def search_load_features(ast:) 23 features = [] 24 25 inspect(ast: ast) do |node| 26 next if node.type != :FCALL 27 next unless LOAD_METHODS.include?(node.children[0]) 28 29 features << get_feature(node: node) 30 end 31 32 features 33 end 34 35 def find_path(feature) 36 RubyVM.resolve_feature_path(feature).last 37 rescue LoadError 38 nil 39 end 40 41 def check_file_dependencies(path:) 42 file_dependencies = Hash.new { |hash, key| hash[key] = [] } 43 ast = RubyVM::AbstractSyntaxTree.parse_file(path) 44 features = search_load_features(ast: ast) 45 46 features.each do |feature| 47 feature_path = find_path(feature) || 48 find_path(File.expand_path(feature, File.dirname(path))) 49 50 next if feature_path.nil? 51 52 file_dependencies[path] << feature_path 53 file_dependencies.merge!(check_file_dependencies(path: feature_path)) 54 end 55 56 file_dependencies 57 end check_file_dependencies.rb
  62. ϑΝΠϧؒͷґଘؔ܎Λௐ΂Δ ࣮ࡍʹಈ͔ͯ͠ΈΔ 1 _ext, fpath = RubyVM.resolve_feature_path('prime') 2 pp check_file_dependencies(path:

    fpath) $ ruby check_file_dependencies.rb {"/Users/siman/.rbenv/versions/2.7.0-dev/lib/ruby/2.7.0/prime.rb"=> ["/Users/siman/.rbenv/versions/2.7.0-dev/lib/ruby/2.7.0/singleton.rb", "/Users/siman/.rbenv/versions/2.7.0-dev/lib/ruby/2.7.0/forwardable.rb"], "/Users/siman/.rbenv/versions/2.7.0-dev/lib/ruby/2.7.0/forwardable.rb"=> ["/Users/siman/.rbenv/versions/2.7.0-dev/lib/ruby/2.7.0/forwardable/impl.rb"]}
  63. ࣦഊ͢Δέʔε Ruby ͸ॊೈͳॻ͖ํ͕ग़དྷΔͷͰɺશͯͷέʔεʹରԠ͢Δͷ͸೉͍͕͠ ॲཧʹࣦഊ͢Δύλʔϯྫ # ม਺Ͱࢦఆ fname = 'set' require

    fname # ͕ࣜ౉͞Ε͍ͯΔ require %w(s e t).join # eval eval('require "set"') ී௨ͷਓ͕ॻ͘Α͏ͳέʔε͚ͩରԠ͓͚ͯ͠͹େମ͸ΧόʔͰ͖Δ
  64. ࣦഊ͢Δέʔε ॲཧʹࣦഊ͢Δύλʔϯྫ # ม਺Ͱࢦఆ fname = 'set' require fname #

    ͕ࣜ౉͞Ε͍ͯΔ require %w(p r i m e).join # eval eval('require "set"') ม਺Ͱͷࢦఆ͸Մೳੑ͕͋ΔͷͰରԠ͍ͨ͠ Ruby ͸ॊೈͳॻ͖ํ͕ग़དྷΔͷͰɺશͯͷέʔεʹରԠ͢Δͷ͸೉͍͕͠ ී௨ͷਓ͕ॻ͘Α͏ͳέʔε͚ͩରԠ͓͚ͯ͠͹େମ͸ΧόʔͰ͖Δ
  65. ࣦഊ͢Δέʔε ॲཧʹࣦഊ͢Δύλʔϯྫ # ม਺Ͱࢦఆ fname = 'prime' require fname #

    ͕ࣜ౉͞Ε͍ͯΔ require %w(s e t).join # eval eval('require "set"') ී௨ͷίʔυͰ͸ͳ͍ͷͰରԠͷ༏ઌ౓͸௿͍ Ruby ͸ॊೈͳॻ͖ํ͕ग़དྷΔͷͰɺશͯͷέʔεʹରԠ͢Δͷ͸೉͍͕͠ ී௨ͷਓ͕ॻ͘Α͏ͳέʔε͚ͩରԠ͓͚ͯ͠͹େମ͸ΧόʔͰ͖Δ
  66. ISeq ISeqΛಘΔͨΊͷίʔυ RubyVM::InstructionSequence.compile_option = false iseq = RubyVM::InstructionSequence.compile("puts 2 +

    2") pp iseq.to_a[13] [1, :RUBY_EVENT_LINE, [:putself], [:putobject, 2], [:putobject, 2], [:send, {:mid=>:+, :flag=>16, :orig_argc=>1}, false, nil], [:send, {:mid=>:puts, :flag=>20, :orig_argc=>1}, false, nil], [:leave]]
  67. ISeq [1, :RUBY_EVENT_LINE, [:putself], [:putobject, 2], [:putobject, 2], [:send, {:mid=>:+,

    :flag=>16, :orig_argc=>1}, false, nil], [:send, {:mid=>:puts, :flag=>20, :orig_argc=>1}, false, nil], [:leave]] ಘΒΕͨ ISeq ΛγϛϡϨʔτ͢Δ͜ͱͰಈ࡞ͷղੳ͕ՄೳʹͳΔ ಺෦ελοΫ
  68. ISeq [1, :RUBY_EVENT_LINE, [:putself], [:putobject, 2], [:putobject, 2], [:send, {:mid=>:+,

    :flag=>16, :orig_argc=>1}, false, nil], [:send, {:mid=>:puts, :flag=>20, :orig_argc=>1}, false, nil], [:leave]] ಘΒΕͨ ISeq ΛγϛϡϨʔτ͢Δ͜ͱͰಈ࡞ͷղੳ͕ՄೳʹͳΔ ಺෦ελοΫ self self ΛελοΫʹ push ͢Δ
  69. ISeq [1, :RUBY_EVENT_LINE, [:putself], [:putobject, 2], [:putobject, 2], [:send, {:mid=>:+,

    :flag=>16, :orig_argc=>1}, false, nil], [:send, {:mid=>:puts, :flag=>20, :orig_argc=>1}, false, nil], [:leave]] ಘΒΕͨ ISeq ΛγϛϡϨʔτ͢Δ͜ͱͰಈ࡞ͷղੳ͕ՄೳʹͳΔ ಺෦ελοΫ self 2 2 ΛελοΫʹ push ͢Δ
  70. ISeq [1, :RUBY_EVENT_LINE, [:putself], [:putobject, 2], [:putobject, 2], [:send, {:mid=>:+,

    :flag=>16, :orig_argc=>1}, false, nil], [:send, {:mid=>:puts, :flag=>20, :orig_argc=>1}, false, nil], [:leave]] ಘΒΕͨ ISeq ΛγϛϡϨʔτ͢Δ͜ͱͰಈ࡞ͷղੳ͕ՄೳʹͳΔ ಺෦ελοΫ self 2 2 2 ΛελοΫʹ push ͢Δ
  71. ISeq [1, :RUBY_EVENT_LINE, [:putself], [:putobject, 2], [:putobject, 2], [:send, {:mid=>:+,

    :flag=>16, :orig_argc=>1}, false, nil], [:send, {:mid=>:puts, :flag=>20, :orig_argc=>1}, false, nil], [:leave]] ಘΒΕͨ ISeq ΛγϛϡϨʔτ͢Δ͜ͱͰಈ࡞ͷղੳ͕ՄೳʹͳΔ ಺෦ελοΫ self 4 + ϝιουͷݺͼग़͠ ϨγʔόʔͱҾ਺ 1ͭΛελοΫ͔ΒऔΓग़ͯ͠ ϝιουͷฦΓ஋ΛελοΫʹ push ͢Δ
  72. ISeq [1, :RUBY_EVENT_LINE, [:putself], [:putobject, 2], [:putobject, 2], [:send, {:mid=>:+,

    :flag=>16, :orig_argc=>1}, false, nil], [:send, {:mid=>:puts, :flag=>20, :orig_argc=>1}, false, nil], [:leave]] ಘΒΕͨ ISeq ΛγϛϡϨʔτ͢Δ͜ͱͰಈ࡞ͷղੳ͕ՄೳʹͳΔ ಺෦ελοΫ nil puts ϝιουͷݺͼग़͠ ϨγʔόʔͱҾ਺ 1ͭΛελοΫ͔ΒऔΓग़ͯ͠ ϝιουͷฦΓ஋ΛελοΫʹ push ͢Δ
  73. ISeq [1, :RUBY_EVENT_LINE, [:putself], [:putobject, 2], [:putobject, 2], [:send, {:mid=>:+,

    :flag=>16, :orig_argc=>1}, false, nil], [:send, {:mid=>:puts, :flag=>20, :orig_argc=>1}, false, nil], [:leave]] ಘΒΕͨ ISeq ΛγϛϡϨʔτ͢Δ͜ͱͰಈ࡞ͷղੳ͕ՄೳʹͳΔ ಺෦ελοΫ ࠓ͍Δείʔϓ͔Βൈ͚ग़͢ ελοΫͷҰ൪্ʹ͋Δ஋Λฦ͢
  74. ׆༻ྫ (ruby-type-profiler) https://github.com/mame/ruby-type-profiler ISeq ΛγϛϡϨʔτ֤ͯ͠ϝιουʹͲͷΑ͏ͳ஋͕౉͞Εͯ ͲͷΑ͏ͳ஋Λฦ͍ͯ͠Δͷ͔ͱ͍͏৘ใΛू໿͢Δ ɾ಺෦Ͱܕ৘ใΛू໿͢ΔͨΊʹಛԽͨ͠ RubyVM ͕࣮૷͞Ε͍ͯΔ 1

    # test.rb 2 def foo(x) 3 if x > 10 4 x.to_s 5 else 6 x.boo() 7 x + 42 8 end 9 end 10 11 foo(42) $ exe/type-profiler test.rb test.rb:6: [error] undefined method: Integer#boo Object#foo :: (Integer) -> (Integer | String)
  75. ࢀߟॻ੶ ʮRuby ͷ͘͠Έ Ruby Under a Microscopeʯ Pat Shaughnessy ஶ

    ౡాߒೋɾ֯୩৴ଠ࿠ ڞ༁ Ruby ͷৄࡉΛ஌Γ͍ͨ৔߹͸ͥͻ
  76. TracePoint Πϕϯτ໊ આ໌ :line ࣜͷධՁ :class ΫϥεఆٛɺಛҟΫϥεఆٛɺϞδϡʔϧఆٛ΁ͷಥೖ :end ΫϥεఆٛɺಛҟΫϥεఆٛɺϞδϡʔϧఆٛ΁ͷऴྃ :call

    Ruby Ͱهड़͞Εͨϝιουͷݺͼग़͠ :return Ruby Ͱهड़͞Εͨϝιουݺͼग़͔͠ΒͷϦλʔϯ :c_call C Ͱهड़͞Εͨϝιουͷݺͼग़͠ :c_return C Ͱهड़͞Εͨϝιουݺͼग़͔͠ΒͷϦλʔϯ :raise ྫ֎ͷൃੜ :b_call ϒϩοΫͷ։࢝ :b_return ϒϩοΫͷऴྃ :thread_begin εϨουͷ։࢝ :thread_end εϨουͷऴྃ :fiber_switch ϑΝΠόʔͷ੾Γସ͑ :script_compiled Ruby ίʔυ͕ ISeq ʹίϯύΠϧ͞Εͨ࣌
  77. TracePoint Πϕϯτ໊ આ໌ :line ࣜͷධՁ :class ΫϥεఆٛɺಛҟΫϥεఆٛɺϞδϡʔϧఆٛ΁ͷಥೖ :end ΫϥεఆٛɺಛҟΫϥεఆٛɺϞδϡʔϧఆٛ΁ͷऴྃ :call

    Ruby Ͱهड़͞Εͨϝιουͷݺͼग़͠ :return Ruby Ͱهड़͞Εͨϝιουݺͼग़͔͠ΒͷϦλʔϯ :c_call C Ͱهड़͞Εͨϝιουͷݺͼग़͠ :c_return C Ͱهड़͞Εͨϝιουݺͼग़͔͠ΒͷϦλʔϯ :raise ྫ֎ͷൃੜ :b_call ϒϩοΫͷ։࢝ :b_return ϒϩοΫͷऴྃ :thread_begin εϨουͷ։࢝ :thread_end εϨουͷऴྃ :fiber_switch ϑΝΠόʔͷ੾Γସ͑ :script_compiled Ruby ίʔυ͕ ISeq ʹίϯύΠϧ͞Εͨ࣌
  78. script_compiled Πϕϯτ ྫ͑͹ɺࠓճಋೖ͞Εͨ script_compiled ΠϕϯτΛ༻͍Δͱɺ࣮ࡍʹͲ͜Ͱιʔείʔυ ͕ϩʔυ͞Ε͔͕ͨΘ͔Γ·͢ɻಛʹɺ eval ͷར༻Λݕ஌͢Δ͜ͱ͕ग़དྷ·͢ɻେن໛ϓ ϩδΣΫτͰɺͳ͔ͥྑ͘Θ͔Βͳ͍ڍಈΛ͢Δɺʮ΋͔ͨ͠͠ΒͲ͔ͬͰҙਤ͠ͳ͍ eval

    ͕͋Δ͔΋ʁʯͱ͍ͬͨͱ͖ʹɺͬͦ͜Γؚ·Ε͍ͯͨ eval Λݕग़͢ΔɺͳͲͱ͍ͬ ͨԠ༻͕Ͱ͖Δ͔΋͠Ε·ͤΜɻ Ҿ༻ݩ: Ruby 2.6 ͷվળΛࣗຫ͍ͨ͠, https://techlife.cookpad.com/entry/2018/12/27/093914 Ruby 2.6 Ͱ৽͘͠௥Ճ͞ΕͨΠϕϯτ Ruby ίʔυ͕ ISeq ʹίϯύΠϧ͞ΕͨλΠϛϯάͰϑοΫͰ͖Δ
  79. script_compiled Πϕϯτ eval Λݕ஌͢ΔͨΊͷ؆қతͳίʔυྫ eval_detector.rb 1 TracePoint.trace(:script_compiled) do |tp| 2

    next if tp.method_id != :eval 3 4 puts "#{tp.path}:#{tp.lineno}: #{tp.eval_script.inspect}" 5 end 6 7 eval("1 + 1")
  80. 1 TracePoint.trace(:script_compiled) do |tp| 2 next if tp.method_id != :eval

    3 4 puts "#{tp.path}:#{tp.lineno}: #{tp.eval_script.inspect}" 5 end 6 7 eval("1 + 1") script_compiled Πϕϯτ eval_detector.rb script_compiled ΠϕϯτΛࢦఆ eval Λݕ஌͢ΔͨΊͷ؆қతͳίʔυྫ
  81. script_compiled Πϕϯτ eval Λݕ஌͢ΔͨΊͷ؆қతͳίʔυྫ eval_detector.rb 1 TracePoint.trace(:script_compiled) do |tp| 2

    next if tp.method_id != :eval 3 4 puts "#{tp.path}:#{tp.lineno}: #{tp.eval_script.inspect}" 5 end 6 7 eval("1 + 1") eval ϝιουҎ֎͸ॲཧΛεΩοϓ
  82. 1 TracePoint.trace(:script_compiled) do |tp| 2 next if tp.method_id != :eval

    3 4 puts "#{tp.path}:#{tp.lineno}: #{tp.eval_script.inspect}" 5 end 6 7 eval("1 + 1") script_compiled Πϕϯτ eval_detector.rb TracePoint#eval_script Ͱ eval ʹ ౉͞ΕͨจࣈྻΛऔಘग़དྷΔ eval Λݕ஌͢ΔͨΊͷ؆қతͳίʔυྫ
  83. script_compiled Πϕϯτ eval_detector.rb:7: "1 + 1" ࣮ߦ݁Ռ eval_detector.rb 1 TracePoint.trace(:script_compiled)

    do |tp| 2 next if tp.method_id != :eval 3 4 puts "#{tp.path}:#{tp.lineno}: #{tp.eval_script.inspect}" 5 end 6 7 eval("1 + 1") eval Λݕ஌͢ΔͨΊͷ؆қతͳίʔυྫ
  84. RubyVM::InstructionSequence.load_iseq 1 class RubyVM::InstructionSequence 2 def self.load_iseq(fpath) 3 iseq =

    RubyVM::InstructionSequence.compile_file(fpath) 4 iseq 5 end 6 end iseq Λͦͷ··ฦ͢ฏ࿨ͳίʔυ
  85. RubyVM::InstructionSequence.load_iseq 1 class RubyVM::InstructionSequence 2 def self.load_iseq(fpath) 3 iseq =

    RubyVM::InstructionSequence.compile_file(fpath) . . . . . . iseq . end . end ແ๏஍ଳ iseq Λฦ͢·Ͱͷؒʹ৭ʑͳॲཧΛڬΉ͜ͱ͕Մೳ
  86. RubyVM::InstructionSequence.load_iseq ྫ͑͹ผͷ iseq ʹஔ͖׵͑ͯΈΔ 1 class RubyVM::InstructionSequence 2 def self.load_iseq(fname)

    3 iseq = RubyVM::InstructionSequence.compile_file(fname) 4 5 _ext, fpath = RubyVM.resolve_feature_path('set') 6 iseq = RubyVM::InstructionSequence.compile_file(fpath) 7 8 iseq 9 end 10 end
  87. RubyVM::InstructionSequence.load_iseq ҙਤͨ͠ϥΠϒϥϦ͕औಘग़དྷͣʹΤϥʔʹͳΔ 1 require 'prime' 2 3 p 2.prime? #=>

    undefined method `prime?' for 2:Integer (NoMethodError) 4 5 set = Set.new 6 set.add(1) 7 p set #=> #<Set: {1}> prime ͷ୅ΘΓʹ set ͕࢖͑ΔΑ͏ʹͳ͍ͬͯΔ
  88. RubyVM::InstructionSequence.load_iseq ྫ͑͹ίϯύΠϧ݁ՌΛΩϟογϡͯ͠ΈΔ 1 BYTECODE_CACHE = Hash.new 2 3 class RubyVM::InstructionSequence

    4 def self.load_iseq(fname) 5 return BYTECODE_CACHE[fname] if BYTECODE_CACHE[fname] 6 7 iseq = RubyVM::InstructionSequence.compile_file(fname) 8 BYTECODE_CACHE[fname] = iseq 9 end 10 end
  89. RubyVM::InstructionSequence.load_iseq Ωϟογϡͷ༗Γແ͠ͰͲͷఔ౓͕͕ࠩग़Δͷ͔ϕϯνϚʔΫΛऔͬͯΈΔ 1 require 'benchmark' 2 3 _ext, fpath =

    RubyVM.resolve_feature_path('ripper') 4 5 Benchmark.bm do |r| 6 r.report 'load' do 7 100_000.times do 8 load fpath 9 end 10 end 11 end
  90. RubyVM::InstructionSequence.load_iseq ϕϯνϚʔΫ݁Ռ user system total real load 7.882627 4.416037 12.298664

    ( 12.338419) user system total real load 0.803963 0.931453 1.735416 ( 1.737521) Ωϟογϡແ͠ Ωϟογϡ༗Γ
  91. ϑΝΠϧؒͷґଘ୳ࡧ 2 ॲཧʹࣦഊ͢Δύλʔϯྫ # ม਺Ͱࢦఆ fname = 'set' require fname

    # ͕ࣜ౉͞Ε͍ͯΔ require %w(s e t).join # eval eval('require "set"') TracePoint Λར༻ͯ͠੩తղੳͰࠔ೉ͩͬͨ෦෼Λղܾ͍ͯ͘͠ ɾrequire ʹ౉͞ΕΔ஋͕Θ͔Βͳ͍໰୊
  92. ϑΝΠϧؒͷґଘ୳ࡧ 2 1 TracePoint.trace(:call) do |tp| 2 next if tp.method_id

    != :require 3 4 bl = caller_locations[1] 5 feature = tp.binding.local_variable_get(tp.parameters.first.last) 6 7 puts "#{bl.path} --> #{feature}" 8 end ґଘϑΝΠϧ୳ࡧεΫϦϓτ ( TracePoint ൛)
  93. ϑΝΠϧؒͷґଘ୳ࡧ 2 1 TracePoint.trace(:call) do |tp| 2 next if tp.method_id

    != :require 3 4 bl = caller_locations[1] 5 feature = tp.binding.local_variable_get(tp.parameters.first.last) 6 7 puts "#{bl.path} --> #{feature}" 8 end ґଘϑΝΠϧ୳ࡧεΫϦϓτ ( TracePoint ൛) call ΠϕϯτΛࢦఆ (Ruby ͷϝιουͷݺͼग़͠) require ʹؔͯ͠͸ rubygems Ͱ࠶ఆٛ͞Ε͍ͯΔͷͰ call ΠϕϯτͰั·͑Δ͜ͱ͕ग़དྷΔ
  94. ϑΝΠϧؒͷґଘ୳ࡧ 2 1 TracePoint.trace(:call) do |tp| 2 next if tp.method_id

    != :require 3 4 bl = caller_locations[1] 5 feature = tp.binding.local_variable_get(tp.parameters.first.last) 6 7 puts "#{bl.path} --> #{feature}" 8 end ґଘϑΝΠϧ୳ࡧεΫϦϓτ ( TracePoint ൛) TracePoint#method_id Ͱݺͼग़͞Εͨϝιου໊Λऔಘ require Ҏ֎͸ॲཧΛεΩοϓ͢Δ
  95. ϑΝΠϧؒͷґଘ୳ࡧ 2 1 TracePoint.trace(:call) do |tp| 2 next if tp.method_id

    != :require 3 4 bl = caller_locations[1] 5 feature = tp.binding.local_variable_get(tp.parameters.first.last) 6 7 puts "#{bl.path} --> #{feature}" 8 end ґଘϑΝΠϧ୳ࡧεΫϦϓτ ( TracePoint ൛) όοΫτϨʔε৘ใ͔Β require Λݺͼग़ͨ͠ՕॴΛಛఆ͢Δ ࠶ఆٛ͞Ε͍ͯΔͷͰ 1 ͭͣΒͯ͠৘ใΛऔΔ
  96. ϑΝΠϧؒͷґଘ୳ࡧ 2 1 TracePoint.trace(:call) do |tp| 2 next if tp.method_id

    != :require 3 4 bl = caller_locations[1] 5 feature = tp.binding.local_variable_get(tp.parameters.first.last) 6 7 puts "#{bl.path} --> #{feature}" 8 end ґଘϑΝΠϧ୳ࡧεΫϦϓτ ( TracePoint ൛) TracePoint#parameters ͱ Binding#local_variable_get Λ ૊Έ߹Θͤͯ require Ͱࢦఆͨ͠ϥΠϒϥϦ໊Λऔಘ
  97. ϑΝΠϧؒͷґଘ୳ࡧ 2 1 TracePoint.trace(:call) do |tp| 2 next if tp.method_id

    != :require 3 4 bl = caller_locations[1] 5 feature = tp.binding.local_variable_get(tp.parameters.first.last) 6 7 puts "#{bl.path} --> #{feature}" 8 end ґଘϑΝΠϧ୳ࡧεΫϦϓτ ( TracePoint ൛) ࠷ޙʹ݁ՌΛग़ྗͯ͠ऴΘΓ
  98. ϑΝΠϧؒͷґଘ୳ࡧ 2 1 fname = 'set' 2 require fname 3

    4 require %w(s e t).join 5 6 eval('require "set"') example.rb example.rb --> set example.rb --> set (eval) --> set
  99. ϑΝΠϧؒͷґଘ୳ࡧ׬શ൛ (load_tracer) https://github.com/siman-man/load_tracer ϑΝΠϧؒͷґଘؔ܎Λௐ΂Δ gem ɾRUBY_VERSION >= '2.7.0' ͕ඞཁ 1

    require 'load_tracer' 2 3 puts LoadTracer.trace(format: :dot) { require 'prime' } $ ruby load_tracer.rb | dot -Tpng -o prime_dep.png $ open prime_dep.png
  100. Ruby ίʔυ ·ͱΊ ߏจ໦ ISeq τʔΫϯྻ TracePoint ݸਓతʹ͸ ISeq ΛγϛϡϨʔτͯ͠৭ʑϑοΫΛೖΕͨ

    StaticTracePoint Έ͍ͨͳͷ͕͋Δͱ໘നͦ͏ͩͱࢥ͏ɻ ੩తղੳ ಈతղੳ StaticTracePoint