# インストール
-
公式ホームページ(http://pypy.org/download.html#installing)を参考に。
- hg clone で落としてくる。
$ hg clone https://bitbucket.org/pypy/pypy
- hg clone で落としてくる。
-
pypyをコンパイルする。 hg cloneしてきたpypy上で、ディレクトリを移動する。
$ cd pypy/pypy/goal
-
pypyをコンパイルする。
$ python ../../rpython/bin/rpython --lldebug -Ojit targetpypystandalone
-
lldbでデバッグしたい場合のコンパイルは以下のコマンド。
$ python ../../rpython/bin/rpython --lldebug -Ojit targetpypystandalone
pypy-cというバイナリが出来上がる!
# lldbデバッグする場合
- 簡単なpythonのファイルを作成する。
- 今回の場合は &ref(tmp.py);
|
|
例 : tmp.pをpypy-cで読んでいく様子をlldbでみていく。
|
|
lldbのための debug symbol は /private/var/の下にできるのでしばらくすると消えてしまいます。 なので PYPY_USESSION_DIR や PYPY_USESSION_KEEP を設定すると[[良さそうです。:http://doc.pypy.org/en/latest/getting-started-dev.html#where-to-start-reading-the-sources]]
# pypy interepreter をpdbデバッグする
- lldbの場合と同様に、tmp.pyを読んでいく様子をみていく。
|
|
- 各オプションについて
-
pdb で break するには
b <filename>:<lineno>
-
pdb で b を消すには
clear <breakpointid>
-
pdb で condition をかけるには
condition <breakpointid> <condition>
-
pdb で condition を消す
condition <breakpointid>
-
毎回コマンドを実行するには
commands <breakpointid>
の後に command を打つ 終わる時に end とか continue を書く
-
# 1日目
# どこでパースしているのか探そうという話
|
|
-
ファイルを開いているはずなので、まずは open を追う。 (lldb) b open
-
絶対パスでpythonモジュールを読み込んでるっぽい。
-
なので先頭が/で無いものが tmp.py だとして break point に condition を付ける
-
br m -c ((char*)$rdi)[0]!='/' 3.1
-
で 3.1 にある open で $rdi の先頭が / じゃないやつを止めとく
-
$rdi に引数な文字列をが残っていたりするので x $rdi とかする
-
pyinteractive.py を読む
-
パーサを読みたい
# pdbデバッグしていく
`$ python -m pdb pypy/bin/pyinteractive.py tmp.py`
- break point に commnads を設定して、ファイル名を出力させながらトレースしていく。
- ほしいファイル名はtmp.py。
/Users/e115747/Desktop/pypy/pypy/bin/pyinteractive.py
- このコードの関数で重要な部分らしい。
- do_start() : Python実行用環境が作られる関数
- doit() : 実際にコードが実行される関数
- これらが main.run_toplevel() に渡されて実行される
95行目 : spacce.setitem()
- 第3引数argvがtmp.pyとなっていた
174行目 : main.run_toplevel()
- 第2引数がdoit()であった場合にPythonコードが実行される
pypy/pypy/interpreter/main.py
-
103行目 : f() が doit() と一致。
-
一連の流れをみてみると、pythonに変換されたコードが返ってきていることがわかった。
# 2日目
# python・pypyのコンパイラの仕組み。
- 図&ref(blackbord.jpg)を載せる。
- pyinteractiveがpythonを呼ぶかpypyに呼ぶかはスペースによって決まるらしい。
- pyinteractive は Python 側にパースなども任せている様子
# スペースの切り替え部分を探す。
pypy/pypy/interpreter/main.py
- このソースの
space.wrap(‘softspace’))
-
でスペースで選択されているらしい。
-
pypyのスペースに切り替えたいなぁ…
-
この段階ではpdbで追っていくと Python VM なコードになったので VM を読むことに。
-
Python VM の frame や pyopcode(python の byte codeの様子)などを追う
pypy/pypy/interpreter/baseobjspace.py
- コードの中身(関数やクラス等)を定義している
pypy/pypy/interpreter/pyframe.py
- フレームが作成される
- フレーム : 関数の戻り値等をスタックしておいたりするやつ
pypy/pypy/interpreter/pyopcode.py
- バイトコードの大部分(printやplus等)が書かれている
frame を作成して exec するところまでは追った。
- execute_frame での pdb のバックトレース
|
|
# テストのソースを編集し、それを動かしながらデバッグしていく。
-
直接読みたいパーツをテストしているコードを動かして、パーツのコードを読んでいくことに。
-
parser が読みたい、ということになってので、これをコピって編集する。
pypy/interpreter/pyparser/test/test_pyparse.py
-
編集したもの -> &ref(hoge.py);
-
hoge.pyでparseする。 $ python hoge.py tmp.py
-
すごい文字列いっぱい出てきた!
-
ここからpdbで読んでいく。 $ python -m pdb hoge.py tmp.py
-
textsrcの中身をprintすると、うまくtmp.pyのsourceがとれている。
pypy/interpreter/pyparser/pyparse.py を読んだ
- 141行目からトークナイズの部分。
pypy/interpreter/pyparser/pytokenizer.py を読んでる。
-
アルファベット、数字、改行、コメントアウト等の判別等〜
-
endDFA
-
決定性有限オートマトン( 状態遷移 )
-
行末を検出するのに使われている。
–
-
230行目 python_opmap はリストになっている
-
演算子がハッシュになっている! -
簡単な計算の流れをみて演算子がハッシュになっているのをみた。
-
簡単なfor文の流れをみて段落がどのように判別されているのかをみた。
-
ソースのインデント調べ終え、tree化されていた。
# バイトコードが生成された。
pypy/interpreter/test/test_compiler.py
-
これを編集して -> &ref(hogest.py);
-
バイトコードが生成された。
$ python hogest.py tmp.py
$ python -m pdb hogest.py tmp.py
- ノードの塊をこれでastにするみたい。 pypy/pypy/interpreter/astcompiler/astbuilder.py
- 時間の関係上この日は、この部分は読まなかった。
# バイトコードをディスパッチに食わせて、tmp.pyを実行できるかを試した
- 移植した部分
pypy/pypy/interpreter/eval.py(33)
- ↑ 内の関数 exec_code()
pypy/pypy/interpreter/main.py
-
↑ 内の関数 ensure__main__(space)
-
他にもあったかな〜〜〜〜?
# 3日目!
コンパイラは、与えられたソースコードをいろいろと変換し、 メモリ上に完全構文木(cst)にしたあと、それを抽象構文気(ast)に変換している。
-
astはpypy/interpreter/astcompiler/astbuilder.py:(52) build_ast()で作られている
-
stmt の type を見て handle していく感じ?
-
例えば expr_node_type は以下のように handle されていった
test -> or_test -> and_test -> not_test -> comparison -> expr -> xor_expr -> and_expr -> shift_expr -> arith_expr -> term -> factor -> power
hanldle_expr
- どんどんif文でchildrenを取っていっている
- children が 1 の場合は children[0] を取ってきて終了
- power まできたら handle_power して handle_atom する
- おそらく、演算などがかかりそうな部分にすべて handler があって、 children が 1である(特に演算が無い場合)は次の演算をチェックする、という形で潜っていく様子
file_input
-
pypy/interpreter/pyparser/data/Grammar2.7の中に、simple_stmtの構成等が書かれていた。 pypy/interpreter/pyparser/pytoken.pyの中に、演算子に対応する一覧が書かれている。
-
type は syms の attribute として管理されているので、内部の値が分かっていてもそれが何に相当るのかは分からない
-
例えば syms.stmt は 322 なのだけれど、 hoge.type = 320 とかだと、hoge は syms の何に相当するのか分からない
-
なので syms の attributes と 値から逆引きできるハッシュを作成して読むなどした
-
おそらく pypy にはそういう util があるはず
-
書いた逆引きコードは以下。
|
|
- ちなみに type の値が 256 以下なら pyop らしい。
|
|
とかも、覚えておくといいかも
fileinputに潜る
- stmt.type : 323 –> stmt
type を識別する巨大な if-elif 文があり
|
|
的な感じで分岐する様子
- handle_expr_stmt
handle_expr の時点での btのログ
|
|
-
y などの変数を ast 化すると Ast.NAME が返ってくる
-
+等の処理は handle_binop とかでやってる
-
ast 初期化の initialize_stateは何かのフラグ? 値が 15 とか 8 とかの決め打ちなので
-
ast.py に AST の定義が書かれているが、これは生成されたコードらしい
生成されたコード pypy/interpreter/astcompiler/ast.py
ast.pyを生成するコード /pypy/interpreter/astcompiler/tools/asdl_py.py
生成の定義等が書かれているやつ← 超重要!! pypy/interpreter/astcompiler/tools/Python.asdl
ソースをpypyの中で適当に書いてると環境が壊れることがあるので注意 最悪hg clone等で再構築