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

レガシーPython散策

 レガシーPython散策

2019 5/18の #PyConK  でのトークです

AnaTofuZ

May 18, 2019
Tweet

More Decks by AnaTofuZ

Other Decks in Programming

Transcript

  1. レガシー
    Python
    散策
    八雲アナグラ @AnaTofuZ 5/18


    PyConKyushu 2019

    View Slide

  2. ໌೔ͷ໾ʹ͸
    ཱͨͳ͍
    ࠓ͙͢ߦ͜͏
    ڭࣨ

    View Slide

  3. self.inroduce()
    • 八雲アナグラ (id:AnaTofuZ)
    • 琉球大学修士1年
    • Okinawa.pm(沖縄Perlユーザーグループ)や

    Perl入学式in沖縄などに出没します
    • Pythonは殆ど書きませんが

    Python製のMercurialをよく使います

    View Slide

  4. • 食後のお昼寝タイム
    • レガシーPythonを題材に

    普段使っているPythonインタプリタが

    どのようにプログラミングされているかを
    探ります
    • C言語が出てきます

    View Slide

  5. • Pythonとは
    • Pythonの歴史
    • Pythonの初期バージョンの特徴
    • C言語での実装に迫る

    View Slide

  6. Python
    Cで書かれたCpython

    View Slide

  7. Cに変換すれば早いやろ!!!な

    Cython
    Python

    View Slide

  8. pythonで書かれたpython pypy
    Python

    View Slide

  9. Javaで書かれたpython Jython
    (最近はcafebabepy)
    Python

    View Slide

  10. python
    • 処理系が無数に乱立している事もあり

    スクリプト言語では用語の使い分けを行っている
    • 大文字の言語「Python」
    • プログラミング言語の仕様
    • 小文字の言語名「python」
    • 処理系の事(基本的にはCで書かれているもの)
    • それ以外の実装は実装名で呼ぶ

    View Slide

  11. Python
    • 歴史的に一番古く、一番使われている

    Cpythonを見ていきます!!!
    • その為、皆さんがコマンドラインで

    よく利用している “python” の実装の話です

    View Slide

  12. • Pythonとは
    • Pythonの歴史
    • Pythonの初期バージョン
    • 動かす
    • 実装を眺める
    • Python3の実装を少し見る

    View Slide

  13. (C)Python
    • 1991年 0.9x
    • 最初に公開されたバージョン
    • 1994年 1.0
    • Python初期バージョン
    • 2000年 2.0
    • Haskellに強い影響を受け出す
    • 2008年 3.0
    • 文字列周りの仕様が大幅に変更される

    View Slide

  14. LL
    • 1987年 Perl1.0リリース
    • 1988年 Perl2.0, Perl3.0
    • 1991年 2月 Python.0.9.0

    3月 Perl4.0
    • 1993年 Ruby開発開始
    • 1994年 1月 Python1.0 

    10月 Perl5.0
    • 1995年 12月 Ruby

    View Slide

  15. [ ] Perl Python
    • Perl5のオブジェクト指向では

    メソッドの第一引数がselfですが、

    これはPythonを参考にして実装されている
    sub client {
    my $self = shift;
    return $self->{client};
    }

    View Slide

  16. CPython
    • CPython内ではtagで0.97も打たれている
    ➡ commit ログも見れる
    ➡ Perlも同じようなリポジトリの運用
    • CPythonをcloneすると大体のバージョンの

    ソースコードがローカルで見る事が出来る

    View Slide

  17. Python
    • 微妙にリポジトリに全てのバージョンが

    残っていない
    • 手軽に試せるのは公式のwebpageから

    ダウンロードできる0.9.1

    https://www.python.org/download/releases/
    early/


    View Slide

  18. Python0.9.1
    • configureではなくMakefileがあり

    HighSierraだと普通にmakeできる
    • 検索するといくつか触っている人がいるので

    幾つかのブログで紹介されている
    • 公式ドキュメントを参照する場合は

    リポジトリに含まれているものを利用する

    View Slide

  19. Python0.9.1
    • manページ
    • TeXで書かれたドキュメント
    • コアライブラリ
    • testall.py

    View Slide

  20. Python0.9.1
    • manページ
    ➡ ほとんど役に立たない
    • TeXで書かれたドキュメント
    ➡ 結構書かれている
    • コアライブラリ
    ➡ 複雑なプログラミングの参考になる
    • testall.py
    ➡ 使い方の参考になる

    View Slide

  21. Python
    • LaTeXで書かれている (書き方が古いらしい)
    • Makefileがあり、makeを実行すると

    xdvi (x window のdviビューワー)が起動する
    • xdviの機能でPostScriptに出力する事が可能
    • 全23ページ

    View Slide

  22. Python0.9.1
    • あるもの
    • Intなどのデータ型
    • リスト、dict
    • dictは開発中で、構文には組み込まれていない
    • オブジェクトシステム、継承
    • 今のPythonとはかなり異なる
    • import機能
    • コアライブラリがついていた

    View Slide

  23. Python0.9.1
    • ないもの
    • タプル
    • map, lambda, filter
    • version1からLispの影響で導入
    • リスト内包記法
    • version2からHaskellの影響で導入
    • ジェネレーター, withなど

    View Slide

  24. コードを書く

    (我々)
    字句解析
    構⽂解析
    バイナリファイル
    ⽣成
    バイナリファイル
    を実⾏

    View Slide

  25. Python
    コードを書く

    (我々)
    字句解析
    構⽂解析 バイトコード⽣成
    バイトコードを
    VMが実⾏

    View Slide

  26. Python0.9.1
    • __init__ が存在しない
    • createメソッドを手で書く世界観
    • クラスの継承は =で書く
    from Split import Split
    class HVSplit() = Split():
    #
    def create(self, (parent, hv)):
    # hv is 0 or 1 for HSplit or VSplit
    self = Split.create(self, parent)
    self.hv = hv
    return self

    View Slide

  27. Python0.9.1
    • dicやタプルなど、実装しているが構文に組み込
    まれておらず使えない機能が多い
    • 文字列は “hoge” ではだめで

    ‘hoge’ なシングルクォートしか使えない

    View Slide

  28. Python
    ୈҰ෦ऴ
    ───
    ੍࡞ɾஶ࡞
    N○K

    View Slide

  29. $ݴޠͰͷ
    ࣮૷ʹ
    ഭ͍͖ͬͯ·͢

    View Slide

  30. • Pythonの処理の流れ
    • コードのおおよその雰囲気
    • オブジェクト構造体
    • 0.9.1のバグを直す
    • 計算について

    View Slide

  31. Python
    • ソースコードをバイトコード化し

    VMが実行する流れは0.9.1から3まで同じ
    • 最適化やAPI、計算方法などには違いがあるが

    大まかな枠組みは既に形成されている
    • バイトコードを確認するdisモジュールが

    既に0.9.1から付随している

    View Slide

  32. CPython
    • Grammarファイルに記述されている
    • EBNF(拡張バッカスナウア記法)っぽいもので定義
    • PythonはMake時に、このファイルから

    決定性有限オートマトンを自動生成し、それを利用する
    • Python公式の0.971は既にGrammarが

    ビルドされている為、自動生成はされない

    View Slide

  33. Python Grammar
    • なんとなく読める気がする…?
    • この結果構文木を作る ( a+b の例 )

    funcdef: 'def' NAME parameters ':' suite
    parameters: '(' [fplist] ')'
    fplist: fpdef (',' fpdef)*
    fpdef: NAME | '(' fplist ')'
    (expr: 

    (term: (NAME: a)), (OP: +), (term: (NAME: b))

    )

    View Slide

  34. • 構文解析の結果から扱いやすい

    バイトコード形式に内部的に変換する

    ➡ 速度向上と実装の分離の為
    • 生成したバイトコードは、計算機をエミュレー
    トするプロセスVMで実行される

    View Slide

  35. PythonVM
    • スタックマシンと呼ばれるVMの作り方
    • 大きいスタックで値をやりとりする
    • RubyのYARVも同じ方式
    • 実際のCPUに似せている方式はレジスタマシン
    • Perl6はこの方式

    View Slide

  36. • バイトコードは実行する命令(オペコード)と

    引数(オペランド)や、データの集まり
    • オペコードは数値に変換される
    • CPUのプログラムカウンタ的に、バイトコード
    を進めながらプログラムを実行していく

    View Slide

  37. Python
    ΦϖίʔυಡΈࠐΈ
    ໋ྩ࣮ߦ Ҿ਺ಡΈࠐΈ

    View Slide

  38. PythonVM
    for (;;) {
    static ticker;
    /* Extract opcode and argument */
    opcode = NEXTOP();
    if (HAS_ARG(opcode))
    oparg = NEXTARG();
    /* Main switch on opcode */
    switch (opcode) {
    case BINARY_ADD:
    w = POP();
    v = POP();
    x = add(v, w);
    DECREF(v);
    DECREF(w);
    PUSH(x);
    break;
    ΦϖίʔυΛ
    औΓग़͠

    View Slide

  39. PythonVM
    for (;;) {
    static ticker;
    /* Extract opcode and argument */
    opcode = NEXTOP();
    if (HAS_ARG(opcode))
    oparg = NEXTARG();
    /* Main switch on opcode */
    switch (opcode) {
    case BINARY_ADD:
    w = POP();
    v = POP();
    x = add(v, w);
    DECREF(v);
    DECREF(w);
    PUSH(x);
    break;
    Ҿ਺͕͋Ε͹Ҿ਺Λ
    औΓग़͠

    View Slide

  40. PythonVM
    for (;;) {
    static ticker;
    /* Extract opcode and argument */
    opcode = NEXTOP();
    if (HAS_ARG(opcode))
    oparg = NEXTARG();
    /* Main switch on opcode */
    switch (opcode) {
    case BINARY_ADD:
    w = POP();
    v = POP();
    x = add(v, w);
    DECREF(v);
    DECREF(w);
    PUSH(x);
    break;
    TXJUDIจͰ
    ໋ྩͷॲཧʹ෼ذ

    View Slide

  41. PythonVM
    case BINARY_ADD:
    w = POP();
    v = POP();
    x = add(v, w);
    DECREF(v);
    DECREF(w);
    PUSH(x);
    break;
    ελοΫ͔Β
    ͭͷม਺ΛऔΓग़͠
    w
    v

    View Slide

  42. PythonVM
    case BINARY_ADD:
    w = POP();
    v = POP();
    x = add(v, w);
    DECREF(v);
    DECREF(w);
    PUSH(x);
    break;
    ͭΛ଍ͯ͠YΛ࡞੒

    View Slide

  43. PythonVM
    case BINARY_ADD:
    w = POP();
    v = POP();
    x = add(v, w);
    DECREF(v);
    DECREF(w);
    PUSH(x);
    break;
    WͱXͷϦϑΝϨϯε
    Χ΢ϯτΛ
    σΫϦϝϯτ͢Δ

    View Slide

  44. PythonVM
    case BINARY_ADD:
    w = POP();
    v = POP();
    x = add(v, w);
    DECREF(v);
    DECREF(w);
    PUSH(x);
    break;
    YΛελοΫʹ͍Εͯऴྃ
    x

    View Slide

  45. Python3
    • Python3では、いちいちループすると遅いので

    GCCの拡張機能のラベルgotoなどを利用し

    スピードの高速化を図っている
    • 他のスクリプト言語も大体同じような実装なの
    で、興味があれば他の言語も見てみましょう

    View Slide

  46. Python main
    main(argc, argv)
    int argc;
    char **argv;
    {
    char *filename = NULL;
    FILE *fp = stdin;
    initargs(&argc, &argv);
    if (argc > 1 && strcmp(argv[1], "-") != 0)
    filename = argv[1];
    if (filename != NULL) {
    if ((fp = fopen(filename, "r")) == NULL) {
    fprintf(stderr, "python: can't open file '%s'\n",
    filename);
    exit(2);
    }
    }
    initall();
    setpythonpath(getpythonpath());
    setpythonargv(argc-1, argv+1);
    goaway(run(fp, filename == NULL ? "" : filename));
    /*NOTREACHED*/
    }

    View Slide

  47. Python main
    main(argc, argv)
    int argc;
    char **argv;
    {
    char *filename = NULL;
    FILE *fp = stdin;
    initargs(&argc, &argv);
    if (argc > 1 && strcmp(argv[1], "-") != 0)
    filename = argv[1];
    ,3$Ͱؔ਺એݴ

    View Slide

  48. Python main
    main(argc, argv)
    int argc;
    char **argv;
    {
    char *filename = NULL;
    FILE *fp = stdin;
    initargs(&argc, &argv);
    if (argc > 1 && strcmp(argv[1], "-") != 0)
    filename = argv[1];
    Ҿ਺ΛϑΝΠϧ໊ʹ
    ઃఆ

    View Slide

  49. Python main
    initall();
    setpythonpath(getpythonpath());
    setpythonargv(argc-1, argv+1);
    goaway(run(fp, filename == NULL ? "" : filename)
    /*NOTREACHED*/
    ࣮ଶ͸SVOͰ
    HPBXBZͰΤϥʔͷ
    ย෇͚

    View Slide

  50. Python
    • Pythonでは様々なオブジェクトが登場します
    • C言語の世界では、これらオブジェクトは

    主に構造体として定義しています

    (構造体 = 複数の型と名前を持つもの)
    • Python3では基本的にPyObjectと呼ばれるもの
    から派生します

    View Slide

  51. Python
    • メモリのヒープ領域に必ず置かれる
    • スタックや静的領域には置かれない
    • その為メモリの中でオブジェクトの

    アドレスが変更される事は無い
    CODE
    DATA
    CONST DATA
    ...
    HEAP
    ...
    STACK
    ...

    View Slide

  52. object
    • python0.9.1ではobject構造体がベース
    • OB_HEADをメンバとして持つ構造体
    • OB_HEADは二種類の変数定義のマクロ
    #define OB_HEAD \
    unsigned int ob_refcnt; \
    struct _typeobject *ob_type;
    typedef struct _object {
    OB_HEAD
    } object;

    View Slide

  53. varobject
    • objectを拡張したvarobject構造体もある
    • PyVarObjectに対応する
    • サイズの概念が追加される
    #define OB_VARHEAD \
    OB_HEAD \
    unsigned int ob_size; /* Number of items in
    variable part */
    typedef struct {
    OB_VARHEAD
    } varobject;

    View Slide

  54. object
    ob_refcnt
    ob_type

    View Slide

  55. object
    ob_refcnt
    ob_type
    ΨϕʔδίϨΫλ
    ϦϑΝϨϯεΧ΢ϯτͷ
    Χ΢ϯλʔ
    Ͳ͔͜Β΋ࢀর͞Ε
    ͳ͘ͳΔ ʹͳΔ
    ͱ

    ղ์͞ΕΔ

    View Slide

  56. GC
    • マクロDECREFでGC管理をしている
    • ob_refcntが0の場合

     DELREFによりtp_deallocで解放
    #define DECREF(op) \
    if (--ref_total, --(op)->ob_refcnt > 0) \
    ; \
    else \
    DELREF(op)
    #define DELREF(op) \

    (*(op)->ob_type->tp_dealloc)((object *)(op))

    View Slide

  57. object
    ob_refcnt
    ob_type

    View Slide

  58. object
    ob_refcnt
    ob_type
    ΦϒδΣΫτͷ
    ܕ৘ใ΁ͷ
    ϙΠϯλ

    View Slide

  59. typedef struct _typeobject {
    OB_VARHEAD
    char *tp_name; /* For printing */
    unsigned int tp_basicsize, tp_itemsize; /* For
    allocation */
    /* Methods to implement standard operations */
    void (*tp_dealloc) FPROTO((object *));
    void (*tp_print) FPROTO((object *, FILE *, int));
    object *(*tp_getattr) FPROTO((object *, char *));
    int (*tp_setattr) FPROTO((object *, char *, object
    int (*tp_compare) FPROTO((object *, object *));
    object *(*tp_repr) FPROTO((object *));
    /* Method suites for standard classes */
    number_methods *tp_as_number;
    sequence_methods *tp_as_sequence;
    mapping_methods *tp_as_mapping;
    } typeobject;

    View Slide

  60. typeobject
    typedef struct _typeobject {
    OB_VARHEAD
    char *tp_name; /* For printing */
    unsigned int tp_basicsize, tp_itemsize; /* For
    allocation */
    /* Methods to implement standard operations */
    void (*tp_dealloc) FPROTO((object *));
    void (*tp_print) FPROTO((object *, FILE *, int));
    object *(*tp_getattr) FPROTO((object *, char *));
    int (*tp_setattr) FPROTO((object *, char *, objec
    int (*tp_compare) FPROTO((object *, object *));
    object *(*tp_repr) FPROTO((object *));
    ܕͷ໊લ৘ใ

    View Slide

  61. typeobject
    typedef struct _typeobject {
    OB_VARHEAD
    char *tp_name; /* For printing */
    unsigned int tp_basicsize, tp_itemsize; /* For
    allocation */
    /* Methods to implement standard operations */
    void (*tp_dealloc) FPROTO((object *));
    void (*tp_print) FPROTO((object *, FILE *, int));
    object *(*tp_getattr) FPROTO((object *, char *));
    int (*tp_setattr) FPROTO((object *, char *, objec
    int (*tp_compare) FPROTO((object *, object *));
    object *(*tp_repr) FPROTO((object *));
    ΦϒδΣΫτͷղ์ʹ
    ར༻ͨؔ͠਺

    View Slide

  62. typeobject
    • void* なこれは、関数ポインタ
    • 関数の参照を代入する事が出来る
    • 構造体に関数ポインタをいれることで

    インターフェース的な機能を実現
    • FPROTOはマクロで、プロトタイプ宣言を抑制
    • 当時のコンパイラはプロトタイプ宣言があると

    解釈出来ないケースもあり、マクロで抑制

    (プロトタイプ宣言…Cで関数定義時に必要)
    void (*tp_dealloc) FPROTO((object *));

    View Slide

  63. typeobject
    typeobject Inttype = {
    OB_HEAD_INIT(&Typetype)
    0,
    "int",
    sizeof(intobject),
    0,
    int_dealloc, /*tp_dealloc*/
    };
    typeobject Floattype = {
    OB_HEAD_INIT(&Typetype)
    0,
    "float",
    sizeof(floatobject),
    0,
    free, /*tp_dealloc*/
    };

    View Slide

  64. typeobject
    typeobject Inttype = {
    OB_HEAD_INIT(&Typetype)
    0,
    "int",
    sizeof(intobject),
    0,
    int_dealloc, /*tp_dealloc*/
    };
    typeobject Floattype = {
    OB_HEAD_INIT(&Typetype)
    0,
    "float",
    sizeof(floatobject),
    0,
    free, /*tp_dealloc*/
    };

    View Slide

  65. typeobject
    typeobject Inttype = {
    OB_HEAD_INIT(&Typetype)
    0,
    "int",
    sizeof(intobject),
    0,
    int_dealloc, /*tp_dealloc*/
    };
    typeobject Floattype = {
    OB_HEAD_INIT(&Typetype)
    0,
    "float",
    sizeof(floatobject),
    0,
    free, /*tp_dealloc*/
    };
    qPBUͰ͸GSFF
    JOUͰ͸JOU@EFBMMPD͕
    UQ@EFBMMPDʹొ࿥
    ΦϒδΣΫτ͝ͱʹ
    ҟͳΔؔ਺Λ
    ಉ͡ΠϯλʔϑΣʔεͰ
    ݺͼग़͕͠Մೳ

    View Slide

  66. typeobject
    typedef struct _typeobject {
    object *(*tp_repr) FPROTO((object *));
    /* Method suites for standard classes */
    number_methods *tp_as_number;
    sequence_methods *tp_as_sequence;
    mapping_methods *tp_as_mapping;
    } typeobject;
    JOU qPBUͷ
    ܭࢉͰར༻͢Δ
    ؔ਺ू߹ͷ

    ߏ଄ମ΁ͷࢀর

    View Slide

  67. number_methods
    • number_methosも四則演算などで使うメソッド
    をデータ型によって変更可能にしている
    static number_methods int_as_number = {
    int_add, /*tp_add*/
    int_sub, /*tp_subtract*/
    int_mul, /*tp_multiply*/
    int_div, /*tp_divide*/
    int_rem, /*tp_remainder*/
    int_pow, /*tp_power*/
    int_neg, /*tp_negate*/
    int_pos, /*tp_plus*/
    };

    View Slide

  68. ( )object
    • python0.9.1ではobject構造体がベース
    • OB_HEADをメンバとして持つ構造体
    • OB_HEADは二種類の変数定義のマクロ
    #define OB_HEAD \
    unsigned int ob_refcnt; \
    struct _typeobject *ob_type;
    typedef struct _object {
    OB_HEAD
    } object;

    View Slide

  69. Python3 PyObject
    • 殆ど構造が同じ
    typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
    } PyObject;
    #define PyObject_VAR_HEAD PyVarObject ob_base;

    View Slide

  70. 0.9.1 IntObject
    • intと言いつつ実際に値が入るob_ivalは

    long型

    typedef struct {
    OB_HEAD
    long ob_ival;
    } intobject;
    extern typeobject Inttype;
    #define is_intobject(op) ((op)->ob_type == &Inttype)

    View Slide

  71. Python3 Int
    • Python3ではlongobjectという名前
    • 大体同じだが、digitの要素1の配列を持つ
    • Cで構造体のメンバに

          可変長配列を設定するテクニック
    struct _longobject {
    PyObject_VAR_HEAD
    digit ob_digit[1];
    };

    View Slide

  72. typedef struct {
    OB_VARHEAD
    object **ob_item;
    } listobject;
    extern typeobject Listtype;
    #define is_listobject(op) ((op)->ob_type == &Listtype)
    • リストの構造体

    View Slide

  73. typedef struct {
    OB_VARHEAD
    object **ob_item;
    } listobject;
    extern typeobject Listtype;
    #define is_listobject(op) ((op)->ob_type == &Listtype)
    ΦϒδΣΫτͷࢀর഑ྻ
    ϙΠϯλͷϙΠϯλ

    • ob_itemはC言語の配列になるので

    O(1)でアクセス可能

    View Slide

  74. ?
    • 基本的にこれらの構造体はポインタでcpythonの
    内部ではやりとりされる

    (ヒープ領域に常に確保されている)
    • 構造体はそれぞれ型が異なるが

    いちいち管理するとスタックの扱いがやっかい

    View Slide

  75. IntObject
    • objectとintObjectの違いはob_ivalの有無
    typedef struct {
    OB_HEAD
    long ob_ival;
    } intobject;
    typedef struct _object {
    OB_HEAD
    } object;

    View Slide

  76. IntObject
    typedef struct {
    OB_HEAD
    long ob_ival;
    } intobject;
    ͜͏ݟΔͱobject

    View Slide

  77. IntObject
    typedef struct {
    OB_HEAD
    long ob_ival;
    } intobject;
    ͜͏ݟΔͱobject
    ͜͏ݟΔͱintobject

    View Slide

  78. ?
    • 必ず構造体の一番上にOB_HEADがあることで

    objectのポインタにキャストする事が出来る

    View Slide

  79. >>> 50 + 60
    Process 79910 stopped
    frame #2: 0x00000001000036b9 python`eval_codeat cev
    839 case BINARY_ADD:
    840 w = POP();
    841 v = POP();
    -> 842 x = add(v, w);
    843 DECREF(v);
    844 DECREF(w);
    845 PUSH(x);

    View Slide

  80. >>> 50 + 60
    (lldb) p v
    (object *) $0 = 0x00000001002007c0
    (lldb) p *v
    (object) $4 = {
    ob_refcnt = 2
    ob_type = 0x0000000100020848
    }
    (lldb) p *(intobject *)v
    (intobject) $3 = {
    ob_refcnt = 2
    ob_type = 0x0000000100020848
    ob_ival = 50
    }
    object型として扱われている
    intobjectに
    キャストすると
    値が⾒える!!

    View Slide

  81. • Pythonの処理の流れ
    • コードのおおよその雰囲気
    • オブジェクト構造体
    • 0.9.1のバグを直す
    • 計算について

    View Slide

  82. Python0.9.1
    • Python0.9.1ではテストの掛け算の部分で死ぬ
    +mumei+anatofuz$ src/python
    >>> print 1*1
    Unhandled exception: run-time error: integer overflow
    Stack backtrace (innermost last):
    File "", line 1

    View Slide

  83. Python0.9.1
    • Python0.9.1ではテストの掛け算の部分で死ぬ
    +mumei+anatofuz$ src/python
    >>> print 1*1
    Unhandled exception: run-time error: integer overflow
    Stack backtrace (innermost last):
    File "", line 1
    _⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈_
    > 1*1でオーバーフロー!! <
     ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄

    View Slide

  84. Python0.9.1
    • grep すると src/intobject.cの中にあるらしい
    • intobjectはPython0.9.1で使われる

    基本のオブジェクト
    +mumei+anatofuz$ grep 'integer overflow' src/*.c
    src/intobject.c:
    err_setstr(OverflowError, "integer overflow");

    View Slide

  85. Python0.9.1
    static object *
    err_ovf()
    {
    err_setstr(OverflowError, "integer overflow");
    return NULL;
    }
    • エラーを文字列として出すだけの関数
    • これが呼び出されている部分を探す
    ➡ intオブジェクトの四則演算で呼びだれている

    View Slide

  86. int_mul
    static object *
    int_mul(v, w)
    intobject *v;
    register object *w;
    {
    register long a, b;
    double x;
    if (!is_intobject(w)) {
    err_badarg();
    return NULL;
    }
    a = v->ob_ival;
    b = ((intobject *)w) -> ob_ival;
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    return newintobject(a * b);
    }

    View Slide

  87. static object *
    int_mul(v, w)
    intobject *v;
    register object *w;
    {
    register long a, b;
    double x;
    if (!is_intobject(w)) {
    err_badarg();
    return NULL;
    }
    a = v->ob_ival;
    b = ((intobject *)w) -> ob_ival;
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    return newintobject(a * b);
    }
    X͕JOUͱͯ͠
    ܭࢉग़དྷΔ͔Λ൑ఆ͢Δ
    int_mul

    View Slide

  88. static object *
    int_mul(v, w)
    intobject *v;
    register object *w;
    {
    register long a, b;
    double x;
    if (!is_intobject(w)) {
    err_badarg();
    return NULL;
    }
    a = v->ob_ival;
    b = ((intobject *)w) -> ob_ival;
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    return newintobject(a * b);
    }
    ΦϒδΣΫτͅɺ͔͆Β
    ஋ΛB Cʹ୅ೖ
    int_mul

    View Slide

  89. static object *
    int_mul(v, w)
    intobject *v;
    register object *w;
    {
    register long a, b;
    double x;
    if (!is_intobject(w)) {
    err_badarg();
    return NULL;
    }
    a = v->ob_ival;
    b = ((intobject *)w) -> ob_ival;
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    return newintobject(a * b);
    }
    ֬ೝͷͨΊʹ
    ֻ͚ࢉΛ࣮ߦ

    Θ͔Γ΍͍͢

    int_mul

    View Slide

  90. static object *
    int_mul(v, w)
    intobject *v;
    register object *w;
    {
    register long a, b;
    double x;
    if (!is_intobject(w)) {
    err_badarg();
    return NULL;
    }
    a = v->ob_ival;
    b = ((intobject *)w) -> ob_ival;
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    return newintobject(a * b);
    }
    int_mul

    View Slide

  91. static object *
    int_mul(v, w)
    intobject *v;
    register object *w;
    {
    register long a, b;
    double x;
    if (!is_intobject(w)) {
    err_badarg();
    return NULL;
    }
    a = v->ob_ival;
    b = ((intobject *)w) -> ob_ival;
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    return newintobject(a * b);
    }
    JOUܕͰܭࢉͨ݁͠Ռ͔Β
    ৽͍͠ΦϒδΣΫτΛฦ٫
    int_mul

    View Slide

  92. Python0.9.1
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    • 掛け算の結果がintの範囲内に収まっていないと

    err_ovf(オーバーフローを示す例外)の

    関数を返す

    View Slide

  93. Python0.9.1
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    • 0x7fffffffが32bitのintの最大値

    (2^31 -1)

    View Slide

  94. Python0.9.1
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    • 0x7fffffffが32bitのintの最大値

    (2^31 -1)
    • 0x80000000が32bitのintの最小値

    View Slide

  95. Python0.9.1
    x = (double)a * (double)b;
    if (x > 0x7fffffff ||

    x < (double) (long) 0x80000000)
    return err_ovf();
    • 0x7fffffffが32bitのintの最大値

    (2^31 -1)
    • 0x80000000が32bitのintの最小値
    • -2^31が正解
    • バグの原因!!!!!

    View Slide

  96. Python0.9.1
    • C言語でプリミティブ型の最大値を見る際は

    型キャストの問題で

    マクロを利用するのが望ましい
    • limits.hというヘッダーにINT_MAXとINT_MIN
    がマクロとして定義されているので使用する

    View Slide

  97. Python0.9.1
    • 適切にマクロを利用して範囲を見る事で

    バグを修正
    #include
    …(中略)…
    x = (double)a * (double)b;
    if (x > INT_MAX || x < INT_MIN)
    return err_ovf();

    View Slide

  98. Python0.9.1
    • 掛け算が出来るように!!!!!!!!!
    +mumei+anatofuz$ src/python
    >>> print 4*5*-1
    -20

    View Slide

  99. Python3
    • 基本的にはPython0.9.1と同じコンセプト
    • 関数ポインタが利用される
    • オブジェクトごとの掛け算の実装
    • オブジェクト2つ取り出して掛け算
    • 本当に? と言われそうなので見ていきます

    View Slide

  100. Python3 (VM)
    • POPとTOPで値を取り出し、関数に渡す
    case TARGET(BINARY_MULTIPLY): {
    PyObject *right = POP();
    PyObject *left = TOP();
    PyObject *res = PyNumber_Multiply(left, right);
    Py_DECREF(left);
    Py_DECREF(right);
    SET_TOP(res);
    if (res == NULL)
    goto error;
    DISPATCH();
    }

    View Slide

  101. Python3 ( )
    • binary_op1でスロットに登録された演算を実行

    (実際はもう少し色々している)
    PyObject *
    PyNumber_Multiply(PyObject *v, PyObject *w)
    {
    PyObject *result =
    binary_op1(v, w, NB_SLOT(nb_multiply));
    return result;
    }

    View Slide

  102. static PyObject *
    binary_op1(PyObject *v, PyObject *w, const int op_slot)
    {
    PyObject *x;
    binaryfunc slotv = NULL;
    binaryfunc slotw = NULL;
    if (v->ob_type->tp_as_number != NULL)
    slotv = NB_BINOP(v->ob_type->tp_as_number,

    op_slot);
    if (slotv) {
    if (slotw && PyType_IsSubtype(

    w->ob_type, v->ob_type)) {
    x = slotw(v, w);
    if (x != Py_NotImplemented)
    return x;

    View Slide

  103. static PyObject *
    binary_op1(PyObject *v, PyObject *w, const int op_slot)
    {
    PyObject *x;
    binaryfunc slotv = NULL;
    binaryfunc slotw = NULL;
    if (v->ob_type->tp_as_number != NULL)
    slotv = NB_BINOP(v->ob_type->tp_as_number,

    op_slot);
    if (slotv) {
    if (slotw && PyType_IsSubtype(

    w->ob_type, v->ob_type)) {
    x = slotw(v, w);
    if (x != Py_NotImplemented)
    return x;

    View Slide

  104. static PyObject *
    binary_op1(PyObject *v, PyObject *w, const int op_slot)
    {
    PyObject *x;
    binaryfunc slotv = NULL;
    binaryfunc slotw = NULL;
    if (v->ob_type->tp_as_number != NULL)
    slotv = NB_BINOP(v->ob_type->tp_as_number,

    op_slot);
    if (slotv) {
    if (slotw && PyType_IsSubtype(

    w->ob_type, v->ob_type)) {
    x = slotw(v, w);
    if (x != Py_NotImplemented)
    return x;
    ֻ͚ࢉͳͲʹରԠͨ͠
    ൪߸͕ೖ͍ͬͯΔ

    View Slide

  105. static PyObject *
    binary_op1(PyObject *v, PyObject *w, const int op_slot)
    {
    PyObject *x;
    binaryfunc slotv = NULL;
    binaryfunc slotw = NULL;
    if (v->ob_type->tp_as_number != NULL)
    slotv = NB_BINOP(v->ob_type->tp_as_number,

    op_slot);
    if (slotv) {
    if (slotw && PyType_IsSubtype(

    w->ob_type, v->ob_type)) {
    x = slotw(v, w);
    if (x != Py_NotImplemented)
    return x;
    TMPUW͸ม਺͚ͩͲʜ

    View Slide

  106. static PyObject *
    binary_op1(PyObject *v, PyObject *w, const int op_slot)
    {
    PyObject *x;
    binaryfunc slotv = NULL;
    binaryfunc slotw = NULL;
    if (v->ob_type->tp_as_number != NULL)
    slotv = NB_BINOP(v->ob_type->tp_as_number,

    op_slot);
    if (slotv) {
    if (slotw && PyType_IsSubtype(

    w->ob_type, v->ob_type)) {
    x = slotw(v, w);
    if (x != Py_NotImplemented)
    return x;
    ؔ਺Έ͍ͨʹݺͼग़͍ͯ͠Δ

    View Slide

  107. • そういえばPython0.9.1はinterface的に

    オブジェクトごと関数をポインタに

    設定していました
    static number_methods int_as_number = {
    int_add, /*tp_add*/
    int_sub, /*tp_subtract*/
    int_mul, /*tp_multiply*/
    int_div, /*tp_divide*/
    int_rem, /*tp_remainder*/
    int_pow, /*tp_power*/
    int_neg, /*tp_negate*/
    int_pos, /*tp_plus*/
    };

    View Slide

  108. • NB_BINOPは配列に設定されている計算用の

    関数を取り出している (指定をslot番号で行う)
    #define NB_BINOP(nb_methods, slot) \
    (*(binaryfunc*)(& ((char*)nb_methods)[slot]))

    View Slide

  109. • 0.9.1と同じようにいろいろな関数を設定!!
    static PyNumberMethods long_as_number = {
    (binaryfunc)long_add, /*nb_add*/
    (binaryfunc)long_sub, /*nb_subtract*/
    (binaryfunc)long_mul, /*nb_multiply*/
    long_mod, /*nb_remainder*/
    long_divmod, /*nb_divmod*/
    long_pow, /*nb_power*/
    (unaryfunc)long_neg, /*nb_negative*/
    long_long, /*tp_positive*/
    (unaryfunc)long_abs, /*tp_absolute*/
    (inquiry)long_bool, /*tp_bool*/

    View Slide

  110. • Python0.9.1と似たような処理!!!!
    static PyObject *
    long_mul(PyLongObject *a, PyLongObject *b)
    {
    PyLongObject *z;
    CHECK_BINOP(a, b);
    /* fast path for single-digit multiplication */
    if (Py_ABS(Py_SIZE(a)) <= 1 && Py_ABS(Py_SIZE(b))
    <= 1) {
    stwodigits v = 

    (stwodigits)(MEDIUM_VALUE(a)) * MEDIUM_VALUE(b);
    return PyLong_FromLongLong((long long)v);
    }
    }

    View Slide

  111. • Pythonのオブジェクトは大体objectを継承
    • キャストして使い、ポインタで基本やりとり
    • Pythonはバイトコードの形に変換される
    • 実行される命令はオブジェクトによって異なる
    • interface的に関数ポインタで制御
    • 大まかな処理はレガシーも同じ!

    View Slide

  112. ( )
    • Pythonの歴史にある程度詳しくなりました
    • Pythonインタプリタがどの様に書かれているか

    ちょっと見てみました
    • Pytonインタプリタも、結局はCのプログラムな
    ので、読解してみて自分だけのPythonに改造し
    てみよう!!

    View Slide

  113. • 基本的に正の数で表現されている為

    負の数は、SIZEを見てよしなに表現する
    /* convert a PyLong of size 1, 0 or -1 to an sdigit */
    #define MEDIUM_VALUE(x) (assert(-1 <= Py_SIZE(x) &&
    Py_SIZE(x) <= 1), \
    Py_SIZE(x) < 0 ? -(sdigit)(x)->ob_digit[0] :
    \
    (Py_SIZE(x) == 0 ? (sdigit)0 :
    \
    (sdigit)(x)->ob_digit[0]))

    View Slide

  114. case PRINT_ITEM:
    v = POP();
    fp = sysgetfile("stdout", stdout);
    if (needspace)
    fprintf(fp, " ");
    if (is_stringobject(v)) {
    char *s = getstringvalue(v);
    int len = getstringsize(v);
    fwrite(s, 1, len, fp);
    if (len > 0 && s[len-1] == '\n')
    needspace = 0;
    else
    needspace = 1;
    }
    else {
    printobject(v, fp, 0);
    needspace = 1;
    }
    DECREF(v);
    break;
    13*/5@*5&.

    QSJOUͷΦϖίʔυ

    View Slide

  115. case PRINT_ITEM:
    v = POP();
    fp = sysgetfile("stdout", stdout);
    if (needspace)
    fprintf(fp, " ");
    if (is_stringobject(v)) {
    char *s = getstringvalue(v);
    int len = getstringsize(v);
    fwrite(s, 1, len, fp);
    if (len > 0 && s[len-1] == '\n')
    needspace = 0;
    else
    needspace = 1;
    }
    else {
    printobject(v, fp, 0);
    needspace = 1;
    }
    DECREF(v);
    break;
    GXSJUFͰ
    TUEPVUඪ४ग़ྗʹ
    ॻ͖ࠐΈ

    View Slide

  116. print
    • Python0.9.1の出力は素朴にfwrite
    • Python3ではバッファなども考慮している為

    こんなにシンプルに実装していない
    (lldb) p len
    (int) $0 = 4
    (lldb) p s
    (char *) $1 = 0x00000001002016a4 "hoge"
    char *s = getstringvalue(v);
    int len = getstringsize(v);
    fwrite(s, 1, len, fp);

    View Slide