32.12. dis — Python バイトコードの逆アセンブラ

Source code: Lib/dis.py


dis モジュールは CPython バイトコード (bytecode) を逆アセンブルすることでバイトコードの解析をサポートします。 このモジュールが入力として受け取る CPython バイトコードはファイル Include/opcode.h に定義されており、 コンパイラとインタプリタが使用しています。

CPython 実装の詳細: バイトコードは CPython インタプリタの実装詳細です! Python のバージョン 間でバイトコードの追加や、削除、変更がないという保証はありません。この モジュールを使用することによって Python の異なる VM または異なるリリース の間で動作すると考えるべきではありません。

例: 以下の関数 myfunc() を考えると

def myfunc(alist):
    return len(alist)

myfunc() の逆アセンブル結果を得るために次のコマンドを使うことができます

>>> dis.dis(myfunc)
  2           0 LOAD_GLOBAL              0 (len)
              3 LOAD_FAST                0 (alist)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE

(“2” は行番号です)。

dis モジュールは次の関数と定数を定義します:

dis.dis([bytesource])

bytesource オブジェクトを逆アセンブルします。 bytesource はモジュール、クラス、関数、あるいはコードオブジェクトのいずれかを示します。 モジュールに対しては、すべての関数を逆アセンブルします。クラスに対しては、すべてのメソッドを逆アセンブルします。 単一のコード列に対しては、バイトコード命令ごとに 1 行を出力します。 オブジェクトが与えられない場合は、最後のトレースバックを逆アセンブルします。

dis.distb([tb])

トレースバックのスタックの先頭の関数を逆アセンブルします。 Noneが渡された場合は最後のトレースバックを使います。例外を引き起こした命令が表示されます。

dis.disassemble(code[, lasti])

コードオブジェクトを逆アセンブルします。 lasti が与えられた場合は、最後の命令を示します。出力は次のようなカラムに分割されます:

  1. 各行の最初の命令に対する行番号。

  2. 現在の命令。 --> として示されます。

  3. ラベル付けされた命令。 >> とともに表示されます。

  4. 命令のアドレス。

  5. 命令コード名。

  6. 命令パラメタ。

  7. パラメタの解釈を括弧で囲んだもの。

パラメタの解釈は、ローカル変数とグローバル変数の名前、定数の値、 分岐先、比較命令を認識します。

dis.disco(code[, lasti])

disassemble() の別名。よりタイプしやすく、以前の Python リリースと互換性があります。

dis.findlinestarts(code)

このジェネレータ関数は、コードオブジェクト codeco_firstlinenoco_lnotab 属性を使い、ソースコード内の行が始まる場所であるオフセットを 求めます。これらは (offset, lineno) の対として生成されます。

dis.findlabels(code)

ジャンプ先であるコードオブジェクト code のすべてのオフセットを 求め、これらのオフセットのリストを返します。

dis.opname

命令コード名のリスト。バイトコードをインデクスに使って参照できます。

dis.opmap

命令コード名をバイトコードに対応づける辞書。

dis.cmp_op

すべての比較命令の名前のリスト。

dis.hasconst

定数パラメタを持つバイトコードのリスト。

dis.hasfree

自由変数にアクセスするバイトコードのリスト。

dis.hasname

名前によって属性にアクセスするバイトコードのリスト。

dis.hasjrel

相対ジャンプ先を持つバイトコードのリスト。

dis.hasjabs

絶対ジャンプ先を持つバイトコードのリスト。

dis.haslocal

ローカル変数にアクセスするバイトコードのリスト。

dis.hascompare

ブール命令のバイトコードのリスト。

32.12.1. Python バイトコード命令

現在 Python コンパイラは次のバイトコード命令を生成します。

STOP_CODE()

コンパイラにコードの終わりを知らせます。インタプリタでは使われません。

NOP()

なにもしないコード。バイトコードオプティマイザでプレースホルダとして使われます。

POP_TOP()

スタックの先頭 (TOS) の要素を取り除きます。

ROT_TWO()

スタックの先頭の 2 つの要素を入れ替えます。

ROT_THREE()

スタックの二番目と三番目の要素の位置を 1 つ上げ、先頭を三番目へ下げます。

ROT_FOUR()

スタックの二番目、三番目および四番目の位置を 1 つ上げ、先頭を四番目に下げます。

DUP_TOP()

スタックの先頭にある参照の複製を作ります。

単項命令はスタックの先頭を取り出して操作を適用し、結果をスタックへプッシュし戻します。

UNARY_POSITIVE()

TOS = +TOS に対応します。

UNARY_NEGATIVE()

TOS = -TOS に対応します。

UNARY_NOT()

TOS = not TOS に対応します。

UNARY_CONVERT()

TOS = `TOS` に対応します。

UNARY_INVERT()

TOS = ~TOS に対応します。

GET_ITER()

TOS = iter(TOS) に対応します。

二項命令はスタックの先頭 (TOS) と先頭から二番目の要素をスタックから取り除きます。 命令を実行し、スタックへ結果をプッシュし戻します。

BINARY_POWER()

TOS = TOS1 ** TOS に対応します。

BINARY_MULTIPLY()

TOS = TOS1 * TOS に対応します。

BINARY_DIVIDE()

from __future__ import division が有効でないときの TOS = TOS1 / TOS に対応します。

BINARY_FLOOR_DIVIDE()

TOS = TOS1 // TOS に対応します。

BINARY_TRUE_DIVIDE()

from __future__ import division が有効なときの TOS = TOS1 / TOS に対応します。

BINARY_MODULO()

TOS = TOS1 % TOS に対応します。

BINARY_ADD()

TOS = TOS1 + TOS に対応します。

BINARY_SUBTRACT()

TOS = TOS1 - TOS に対応します。

BINARY_SUBSCR()

TOS = TOS1[TOS] に対応します。

BINARY_LSHIFT()

TOS = TOS1 << TOS に対応します。

BINARY_RSHIFT()

TOS = TOS1 >> TOS に対応します。

BINARY_AND()

TOS = TOS1 & TOS に対応します。

BINARY_XOR()

TOS = TOS1 ^ TOS に対応します。

BINARY_OR()

TOS = TOS1 | TOS に対応します。

インプレース命令は TOS と TOS1 を取り除いて結果をスタックへプッシュするという点で二項命令と似ています。 しかし、TOS1 がインプレース命令をサポートしている場合には操作が直接 TOS1 に行われます。 また、操作結果の TOS は (常に同じというわけではありませんが) 元の TOS1 と同じオブジェクトになることが多いです。

INPLACE_POWER()

インプレースの TOS = TOS1 ** TOS に対応します。

INPLACE_MULTIPLY()

インプレースの TOS = TOS1 * TOS に対応します。

INPLACE_DIVIDE()

from __future__ import division が有効でないときのインプレースの TOS = TOS1 / TOS に対応します。

INPLACE_FLOOR_DIVIDE()

インプレースの TOS = TOS1 // TOS に対応します。

INPLACE_TRUE_DIVIDE()

from __future__ import division が有効なときのインプレースの TOS = TOS1 / TOS に対応します。

INPLACE_MODULO()

インプレースの TOS = TOS1 % TOS に対応します。

INPLACE_ADD()

インプレースの TOS = TOS1 + TOS に対応します。

INPLACE_SUBTRACT()

インプレースの TOS = TOS1 - TOS に対応します。

INPLACE_LSHIFT()

インプレースの TOS = TOS1 << TOS に対応します。

INPLACE_RSHIFT()

インプレースの TOS = TOS1 >> TOS に対応します。

INPLACE_AND()

インプレースの TOS = TOS1 & TOS に対応します。

INPLACE_XOR()

インプレースの TOS = TOS1 ^ TOS に対応します。

INPLACE_OR()

インプレースの TOS = TOS1 | TOS に対応します。

スライス命令コードは最大 3 つのパラメタを取ります。

SLICE+0()

TOS = TOS[:] に対応します。

SLICE+1()

TOS = TOS1[TOS:] に対応します。

SLICE+2()

TOS = TOS1[:TOS] に対応します。

SLICE+3()

TOS = TOS2[TOS1:TOS] に対応します。

スライス代入はさらにもう 1 つのパラメタを必要とします。 他の文と同じく、これらはスタックに何もプッシュしません。

STORE_SLICE+0()

TOS[:] = TOS1 に対応します。

STORE_SLICE+1()

TOS1[TOS:] = TOS2 に対応します。

STORE_SLICE+2()

TOS1[:TOS] = TOS2 に対応します。

STORE_SLICE+3()

TOS2[TOS1:TOS] = TOS3 に対応します。

DELETE_SLICE+0()

del TOS[:] に対応します。

DELETE_SLICE+1()

del TOS1[TOS:] に対応します。

DELETE_SLICE+2()

del TOS1[:TOS] に対応します。

DELETE_SLICE+3()

del TOS2[TOS1:TOS] に対応します。

STORE_SUBSCR()

TOS1[TOS] = TOS2 に対応します。

DELETE_SUBSCR()

del TOS1[TOS] に対応します。

その他の命令コード。

PRINT_EXPR()

対話モードのための式文に対応します。TOS はスタックから取り除かれ表示されます。 非対話モードにおいては、式文は POP_TOP で終了しています。

PRINT_ITEM()

sys.stdout に束縛されたファイル互換オブジェクトに対して TOS を出力します。 print 文の各要素に対してこのような命令が一つずつあります。

PRINT_ITEM_TO()

PRINT_ITEM と似ていますが、TOS から二番目の要素を TOS にあるファイル互換オブジェクトへ出力します。 これは拡張 print 文で使われます。

PRINT_NEWLINE()

sys.stdout へ改行を表示します。 これは print 文がコンマで終わっていない場合に print 文の最後の命令として生成されます。

PRINT_NEWLINE_TO()

PRINT_NEWLINE と似ていますが、TOSのファイル互換オブジェクトに改行を表示します。 これは拡張 print 文で使われます。

BREAK_LOOP()

break 文によってループを終了します。

CONTINUE_LOOP(target)

continue 文によってループを継続します。 target はジャンプするアドレスです (アドレスは FOR_ITER 命令でなければなりません)。

LIST_APPEND(i)

list.append(TOS[-i], TOS) を呼びます。リスト内包表記を実装するために使われます。 要素が取り除かれる間、リストオブジェクトはスタックに残るので、ループの 反復をさらに行えます。

LOAD_LOCALS()

現在のスコープのローカルな名前空間 (locals) への参照をスタックにプッシュします。 これはクラス定義のためのコードで使われます: クラス本体が評価された後、locals はクラス定義へ渡されます。

RETURN_VALUE()

関数の呼び出し元へ TOS を返します。

YIELD_VALUE()

TOS をポップし、それをジェネレータ (generator) から yield します。

IMPORT_STAR()

'_' で始まっていないすべてのシンボルをモジュール TOS から直接ローカル名前空間へロードします。 モジュールはすべての名前をロードした後にポップされます。 この命令コードは from module import * に対応します。

EXEC_STMT()

exec TOS2,TOS1,TOS に対応します。コンパイラは指定されなかったオプションのパラメタを None で埋めます。

POP_BLOCK()

ブロックスタックからブロックを一つ取り除きます。 フレームごとにブロックのスタックがあり、ネストしたループや try 文などを表しています。

END_FINALLY()

finally 節を終了します。 インタプリタは例外を再送出しなければならないかどうか、あるいは、 関数から return して外側の次のブロックに続くかどうかを再度判断します。

BUILD_CLASS()

新しいクラスオブジェクトを作成します。TOSはメソッド辞書、TOS1は基底クラスの名前のタプル、TOS2はクラス名です。

SETUP_WITH(delta)

この命令コードは、with ブロックが開始する前にいくつかの命令を行います。 まず、コンテキストマネージャから __exit__() をロードし、 後から WITH_CLEANUP で使うためにスタックにプッシュします。 そして、 __enter__() が呼び出され、 delta を指す finally ブロックがプッシュされます。最後に、enter メソッドを呼び出した 結果がスタックにプッシュされます。次の命令コードはこれを無視 (POP_TOP) するか、変数に保存 (STORE_FAST, STORE_NAME, または UNPACK_SEQUENCE) します。

WITH_CLEANUP()

with 式ブロックを抜けるときに、スタックをクリーンアップします。 スタックの先頭は 1–3 個の値で、それらはなぜ/どのように finally 節に 到達したかを表しています:

  • TOP = None
  • (TOP, SECOND) = (WHY_{RETURN,CONTINUE}), retval
  • TOP = WHY_*; no retval below it
  • (TOP, SECOND, THIRD) = exc_info()

これらの値の下には、コンテキストマネージャーの __exit__() 結合メソッド (bound method) である EXIT があります。

最後のケースでは、 EXIT(TOP, SECOND, THIRD) が呼ばれ、それ以外では EXIT(None, None, None) が呼ばれます。

EXIT はスタックから取り除かれ、その上の値は順序を維持したまま残されます。 加えて、スタックが例外処理中であることを示し、 かつ 関数呼び出しが true 値を返した場合、 END_FINALLY が例外を再送出することを防ぐため、この情報は削除されます (“zapped”)。 (しかし、 non-local goto は再開されます)

以下の命令コードはすべて引数を必要とします。引数は 2 バイトで、最上位バイトが後になります。

STORE_NAME(namei)

name = TOS に対応します。 namei はコードオブジェクトの属性 co_names における name のインデクスです。 コンパイラは可能ならば STORE_FAST または STORE_GLOBAL を使おうとします。

DELETE_NAME(namei)

del name に対応します。 namei はコードオブジェクトの co_names 属性へのインデクスです。

UNPACK_SEQUENCE(count)

TOS を count 個の個別の値にアンパックして、右から左の順にスタックに置きます。

DUP_TOPX(count)

count 個の要素を順番を保ちながら複製します。 実装上の制限から、 count は 1以上 から 5 以下でなければなりません。

STORE_ATTR(namei)

TOS.name = TOS1 に対応します。 nameico_names における名前のインデクスです。

DELETE_ATTR(namei)

del TOS.name に対応します。 co_names へのインデクスとして namei を使います。

STORE_GLOBAL(namei)

STORE_NAME と同じように動作しますが、 name をグローバルとして保存します。

DELETE_GLOBAL(namei)

DELETE_NAME と同じように動作しますが、グローバルの name を削除します。

LOAD_CONST(consti)

co_consts[consti] をスタックにプッシュします。

LOAD_NAME(namei)

co_names[namei] に関連付けられた値をスタックにプッシュします。

BUILD_TUPLE(count)

スタックから count 個の要素を消費してタプルを作り出し、できたタプルをスタックにプッシュします。

BUILD_LIST(count)

BUILD_TUPLE と同じように動作しますが、リストを作り出します。

BUILD_SET(count)

BUILD_TUPLE と同じように動作しますが、set を作り出します。

バージョン 2.7 で追加.

BUILD_MAP(count)

スタックに新しい辞書オブジェクトをプッシュします。 辞書は count 個のエントリを持つサイズに設定されます。

LOAD_ATTR(namei)

TOS を getattr(TOS, co_names[namei]) と入れ替えます。

COMPARE_OP(opname)

ブール命令を実行します。命令名は cmp_op[opname] にあります。

IMPORT_NAME(namei)

モジュール co_names[namei] をインポートします。 TOS と TOS1 がポップされ、 __import__()fromlistlevel 引数になります。 モジュールオブジェクトはスタックへプッシュされます。現在の名前空間は影響されません: 適切な import 文のためには、後続の STORE_FAST 命令が名前空間を変更します。

IMPORT_FROM(namei)

TOS にあるモジュールから属性 co_names[namei] をロードします。 作成されたオブジェクトはスタックにプッシュされ、後続の STORE_FAST 命令によって保存されます。

JUMP_FORWARD(delta)

バイトコードカウンタを delta だけ増加させます。

POP_JUMP_IF_TRUE(target)

TOS が真ならば、バイトコードカウンタを target に設定します。 TOS はポップされます。

POP_JUMP_IF_FALSE(target)

TOS が偽ならば、バイトコードカウンタを target に設定します。 TOS はポップされます。

JUMP_IF_TRUE_OR_POP(target)

TOS が真ならば、バイトコードカウンタを target に設定し、TOS は スタックに残されます。そうでない (TOS が偽) なら、TOS はポップされます。

JUMP_IF_FALSE_OR_POP(target)

TOS が偽ならば、バイトコードカウンタを target に設定し、TOS は スタックに残されます。そうでない (TOS が真) なら、TOS はポップされます。

JUMP_ABSOLUTE(target)

バイトコードカウンタを target に設定します。

FOR_ITER(delta)

TOS はイテレータです。その next() メソッドを呼び出します。 新しい値が yield された場合は、それをスタックにプッシュします (イテレータはその下に残されます)。 イテレータの呼び出しで要素が尽きたことが示された場合は、 TOS がポップされます。 そして、バイトコードカウンタが delta だけ増やされます。

LOAD_GLOBAL(namei)

co_names[namei] という名前のグローバルをスタック上にロードします。

SETUP_LOOP(delta)

ループのためのブロックをブロックスタックにプッシュします。 ブロックは現在の命令から delta バイトの大きさを占めます。

SETUP_EXCEPT(delta)

try-except 節から try ブロックをブロックスタックにプッシュします。 delta は最初の except ブロックを指します。

SETUP_FINALLY(delta)

try-except 節から try ブロックをブロックスタックにプッシュします。 delta は finally ブロックを指します。

STORE_MAP()

key, value のペアを辞書に格納します。 key と value をポップする一方、辞書はスタックに残されます。

LOAD_FAST(var_num)

ローカルな co_varnames[var_num] への参照をスタックにプッシュします。

STORE_FAST(var_num)

TOS をローカルな co_varnames[var_num] の中に保存します。

DELETE_FAST(var_num)

ローカルな co_varnames[var_num] を削除します。

LOAD_CLOSURE(i)

セルと自由変数の記憶領域のスロット i に含まれるセルへの参照をプッシュします。 ico_cellvars の長さより小さければ、変数の名前は co_cellvars[i] です。 そうでなければ co_freevars[i - len(co_cellvars)] です。

LOAD_DEREF(i)

セルと自由変数の記憶領域のスロット i に含まれるセルをロードします。 セルが持つオブジェクトへの参照をスタックにプッシュします。

STORE_DEREF(i)

セルと自由変数の記憶領域のスロット i に含まれるセルへTOSを保存します。

SET_LINENO(lineno)

この命令コードは廃止されました。

RAISE_VARARGS(argc)

例外を発生させます。 argc は raise 文へ与えるパラメタの数を 0 から 3 の 範囲で示します。 ハンドラは TOS2 をトレースバック、TOS1 をパラメタ、TOS を例外として探します。

CALL_FUNCTION(argc)

関数を呼び出します。 argc の下位バイトは位置パラメタの数を、上位バイトはキーワードパラメタの数を示します。 スタック上では、最初にキーワードパラメタが見つかります。 それぞれのキーワード引数に対しては、値がキーより上に来ます。 スタック上のキーワードパラメタの下に位置パラメタがあり、最も右のパラメタが先頭になります。 スタック上のパラメタの下には、呼び出される関数オブジェクトが置かれます。 すべての関数引数をポップし、関数自体もスタックから取り除き、戻り値をプッシュします。

MAKE_FUNCTION(argc)

新しい関数オブジェクトをスタックにプッシュします。 TOS は関数に関連付けられたコードです。 関数オブジェクトは TOS の下にある argc デフォルトパラメタをもつように定義されます。

MAKE_CLOSURE(argc)

新しい関数オブジェクトを作り出し、その func_closure スロットを設定し、スタックにプッシュします。 TOS は関数に関連付けられたコードで、TOS1 はクロージャの自由変数に対するセルを格納したタプルです。 関数はセルの前にある argc デフォルトパラメタも持っています。

BUILD_SLICE(argc)

スライスオブジェクトをスタックにプッシュします。 argc は2あるいは3でなければなりません。 2 ならば slice(TOS1, TOS) がプッシュされます。 3 ならば slice(TOS2, TOS1, TOS) がプッシュされます。 これ以上の情報については、 slice() 組み込み関数を参照してください。

EXTENDED_ARG(ext)

デフォルトの 2 バイトに収まりきらない大きな引数を持つあらゆる命令コードの前に置かれます。 ext は追加の 2 バイトを保持し、後続の命令コードの引数と組み合わされます。 それらは 4 バイト引数を構成し、 ext はその最上位バイトです。

CALL_FUNCTION_VAR(argc)

関数を呼び出します。 argcCALL_FUNCTION と同じように解釈されます。 スタックの先頭の要素は可変引数リストを含んでおり、その後にキーワード引数と位置引数が続きます。

CALL_FUNCTION_KW(argc)

関数を呼び出します。 argcCALL_FUNCTION と同じように解釈されます。 スタックの先頭の要素はキーワード引数辞書を含んでおり、その後に明示的なキーワード引数と位置引数が続きます。

CALL_FUNCTION_VAR_KW(argc)

関数を呼び出します。 argcCALL_FUNCTION と同じように解釈されます。 スタックの先頭の要素はキーワード引数辞書を含んでおり、その後に変数引数のタプルが続き、 さらに明示的なキーワード引数と位置引数が続きます。

HAVE_ARGUMENT()

これは実際の命令コードではありません。引数を取らない命令コード < HAVE_ARGUMENT と、 引数を取る命令コード >= HAVE_ARGUMENT の分割行を表します。