vm2gol v2 (58) _debug でブレークポイントを指定できるようにした



今回はブレークポイントを指定できるようにしたのと、後はリファクタリングです。

_debug でブレークポイントを指定

これまで、プログラムの特定の箇所の動作を観察してデバッグしたい場合次のような手順を踏んでいました。

  • まず適当に実行(最初からステップ実行するなど)して、調べたい場所が何ステップ目あたりかを調べる
  • vgvm.rb を直接書き換え、どのステップまでスキップするか指定する
  • 実行するとそのステップまでスキップされるので、そこからステップ実行する

参考: (30) 生存カウント / VRAM から値を取得 / ステップ数を表示

スキップできなかった状態に比べればこれでもかなりの進歩だったわけですが、 さらに便利になるようにブレークポイントを指定できるようにしました。


ステップ実行を始めたい箇所に _debug(); と書いて実行すると、

func main() {
  var a = 1;
  var b = 10;
  set a = a + 2;
  _debug();      // ここからステップ実行開始
  set a = a + b;
}

指定した箇所までダンプ表示がスキップされ、 次の図のように _debug 命令の箇所で止まります。

あとはこれまで通り Enter キーを押してステップ実行すればOK。

f:id:sonota88:20210504065415p:plain


これ、ほんとにちょっとした修正で済むのでたいへんお買い得です。もっと早くやっておけばよかった。

以下の VM の修正を見ての通り、 _debug 命令が来たら Vm#debug を true にして、 Vm#debug が true になっていたらステップ実行する、それだけ。

--- a/vgvm.rb
+++ b/vgvm.rb
@@ -135,6 +135,7 @@ class Vm
     @bp = stack_size - 1 # base pointer
 
     @step = 0
+    @debug = false
   end
 
   def test?
@@ -178,6 +179,7 @@ class Vm
     when "set_vram"  then set_vram()  ; @pc += 1
     when "get_vram"  then get_vram()  ; @pc += 1
     when "_cmt"      then               @pc += 1
+    when "_debug"    then _debug()    ; @pc += 1
     else
       raise "Unknown opcode (#{opcode})"
     end
@@ -199,7 +201,7 @@ class Vm
       return if do_exit
 
       unless test?
-        if ENV.key?("STEP")
+        if ENV.key?("STEP") || @debug
           dump()
           $stdin.gets
           # $stdin.gets if @step >= 600
@@ -432,6 +434,10 @@ class Vm
       raise not_yet_impl("arg2", arg2)
     end
   end
+
+  def _debug
+    @debug = true
+  end
 end
 
 if $PROGRAM_NAME == __FILE__

ほとんどそのまま受け渡すだけなのでコンパイラ部分も少しの修正でOK。

to_fn_arg_disp, to_lvar_disp

lvar_addr = to_lvar_addr(lvar_names, lvar_name)
puts "  cp 0 #{lvar_addr}"

のように使っていたやつです。

この lvar_addr には [bp:-2] のような文字列が入るわけですが、これって「アドレス」なんだっけ?   値をセットする宛先だからアドレスと呼んでもダメじゃなさそうだけど…… 詳しくいえば「宛先アドレスの bp 相対間接参照表現」みたいになるんでしょうか……。

みたいな感じでモヤモヤしていて気になっていました。

そこで、モヤモヤしなくて済むような名前と処理内容に変えました。

[bp:-2] のような文字列を返すのではなく、 displacement の値 -2 だけを返すようにすれば、次のように書けます。

  disp = to_lvar_disp(lvar_names, lvar_name)
  puts "cp 0 [bp:#{disp}]"

displacement という、呼び方が定まっているものを返しているわけですから、返り値を受ける変数名も素直に disp とすればよいと(略しているのは置いといて)。

そもそもは何度も同じような処理を繰り返していて煩雑だから共通化していたわけですが、こういう場合は DRY であることよりも意味が明確でモヤモヤしない方が嬉しいです。

また、修正前は

  • displacement を求める
  • 文字列(間接参照表現)を組み立てる

という2つの処理を行っていたのが、責務も単純になり、その面でも良かったのかなと。

その他の修正

他に気になっていた部分のリファクタリング

  • メソッドの並び順の修正
  • その他細かいの
    • parse_args まわりを簡素化