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

railsdm2019.pdf

 railsdm2019.pdf

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