レガシーPython散策

 レガシーPython散策

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

Ffd3f0ebea474176dfbe876216a793f9?s=128

AnaTofuZ

May 18, 2019
Tweet

Transcript

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

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

  3. self.inroduce() • 八雲アナグラ (id:AnaTofuZ) • 琉球大学修士1年 • Okinawa.pm(沖縄Perlユーザーグループ)や
 Perl入学式in沖縄などに出没します •

    Pythonは殆ど書きませんが
 Python製のMercurialをよく使います
  4. • 食後のお昼寝タイム • レガシーPythonを題材に
 普段使っているPythonインタプリタが
 どのようにプログラミングされているかを 探ります • C言語が出てきます

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

  6. Python Cで書かれたCpython

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

  8. pythonで書かれたpython pypy Python

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

  10. python • 処理系が無数に乱立している事もあり
 スクリプト言語では用語の使い分けを行っている • 大文字の言語「Python」 • プログラミング言語の仕様 • 小文字の言語名「python」

    • 処理系の事(基本的にはCで書かれているもの) • それ以外の実装は実装名で呼ぶ
  11. Python • 歴史的に一番古く、一番使われている
 Cpythonを見ていきます!!! • その為、皆さんがコマンドラインで
 よく利用している “python” の実装の話です

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

    • Python3の実装を少し見る
  13. (C)Python • 1991年 0.9x • 最初に公開されたバージョン • 1994年 1.0 • Python初期バージョン

    • 2000年 2.0 • Haskellに強い影響を受け出す • 2008年 3.0 • 文字列周りの仕様が大幅に変更される
  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
  15. [ ] Perl Python • Perl5のオブジェクト指向では
 メソッドの第一引数がselfですが、
 これはPythonを参考にして実装されている sub client

    { my $self = shift; return $self->{client}; }
  16. CPython • CPython内ではtagで0.97も打たれている ➡ commit ログも見れる ➡ Perlも同じようなリポジトリの運用 • CPythonをcloneすると大体のバージョンの


    ソースコードがローカルで見る事が出来る
  17. Python • 微妙にリポジトリに全てのバージョンが
 残っていない • 手軽に試せるのは公式のwebpageから
 ダウンロードできる0.9.1
 https://www.python.org/download/releases/ early/


  18. Python0.9.1 • configureではなくMakefileがあり
 HighSierraだと普通にmakeできる • 検索するといくつか触っている人がいるので
 幾つかのブログで紹介されている • 公式ドキュメントを参照する場合は
 リポジトリに含まれているものを利用する

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

  20. Python0.9.1 • manページ ➡ ほとんど役に立たない • TeXで書かれたドキュメント ➡ 結構書かれている •

    コアライブラリ ➡ 複雑なプログラミングの参考になる • testall.py ➡ 使い方の参考になる
  21. Python • LaTeXで書かれている (書き方が古いらしい) • Makefileがあり、makeを実行すると
 xdvi (x window のdviビューワー)が起動する

    • xdviの機能でPostScriptに出力する事が可能 • 全23ページ
  22. Python0.9.1 • あるもの • Intなどのデータ型 • リスト、dict • dictは開発中で、構文には組み込まれていない •

    オブジェクトシステム、継承 • 今のPythonとはかなり異なる • import機能 • コアライブラリがついていた
  23. Python0.9.1 • ないもの • タプル • map, lambda, filter •

    version1からLispの影響で導入 • リスト内包記法 • version2からHaskellの影響で導入 • ジェネレーター, withなど
  24. コードを書く
 (我々) 字句解析 構⽂解析 バイナリファイル ⽣成 バイナリファイル を実⾏

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

  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
  27. Python0.9.1 • dicやタプルなど、実装しているが構文に組み込 まれておらず使えない機能が多い • 文字列は “hoge” ではだめで
 ‘hoge’ なシングルクォートしか使えない

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

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

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

  31. Python • ソースコードをバイトコード化し
 VMが実行する流れは0.9.1から3まで同じ • 最適化やAPI、計算方法などには違いがあるが
 大まかな枠組みは既に形成されている • バイトコードを確認するdisモジュールが
 既に0.9.1から付随している

  32. CPython • Grammarファイルに記述されている • EBNF(拡張バッカスナウア記法)っぽいもので定義 • PythonはMake時に、このファイルから
 決定性有限オートマトンを自動生成し、それを利用する • Python公式の0.971は既にGrammarが


    ビルドされている為、自動生成はされない
  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))
 )
  34. • 構文解析の結果から扱いやすい
 バイトコード形式に内部的に変換する
 ➡ 速度向上と実装の分離の為 • 生成したバイトコードは、計算機をエミュレー トするプロセスVMで実行される

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

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

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

  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; ΦϖίʔυΛ औΓग़͠
  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; Ҿ਺͕͋Ε͹Ҿ਺Λ औΓग़͠
  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จͰ ໋ྩͷॲཧʹ෼ذ
  41. PythonVM case BINARY_ADD: w = POP(); v = POP(); x

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

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

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

    = add(v, w); DECREF(v); DECREF(w); PUSH(x); break; YΛελοΫʹ͍Εͯऴྃ x
  45. Python3 • Python3では、いちいちループすると遅いので
 GCCの拡張機能のラベルgotoなどを利用し
 スピードの高速化を図っている • 他のスクリプト言語も大体同じような実装なの で、興味があれば他の言語も見てみましょう

  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 ? "<stdin>" : filename)); /*NOTREACHED*/ }
  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$Ͱؔ਺એݴ
  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]; Ҿ਺ΛϑΝΠϧ໊ʹ ઃఆ
  49. Python main initall(); setpythonpath(getpythonpath()); setpythonargv(argc-1, argv+1); goaway(run(fp, filename == NULL

    ? "<stdin>" : filename) /*NOTREACHED*/ ࣮ଶ͸SVOͰ HPBXBZͰΤϥʔͷ ย෇͚
  50. Python • Pythonでは様々なオブジェクトが登場します • C言語の世界では、これらオブジェクトは
 主に構造体として定義しています
 (構造体 = 複数の型と名前を持つもの) •

    Python3では基本的にPyObjectと呼ばれるもの から派生します
  51. Python • メモリのヒープ領域に必ず置かれる • スタックや静的領域には置かれない • その為メモリの中でオブジェクトの
 アドレスが変更される事は無い CODE DATA

    CONST DATA ... HEAP ... STACK ...
  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;
  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;
  54. object ob_refcnt ob_type

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


    ղ์͞ΕΔ
  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))
  57. object ob_refcnt ob_type

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

  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;
  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 *)); ܕͷ໊લ৘ใ
  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 *)); ΦϒδΣΫτͷղ์ʹ ར༻ͨؔ͠਺
  62. typeobject • void* なこれは、関数ポインタ • 関数の参照を代入する事が出来る • 構造体に関数ポインタをいれることで
 インターフェース的な機能を実現 •

    FPROTOはマクロで、プロトタイプ宣言を抑制 • 当時のコンパイラはプロトタイプ宣言があると
 解釈出来ないケースもあり、マクロで抑制
 (プロトタイプ宣言…Cで関数定義時に必要) void (*tp_dealloc) FPROTO((object *));
  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*/ };
  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*/ };
  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ʹొ࿥ ΦϒδΣΫτ͝ͱʹ ҟͳΔؔ਺Λ ಉ͡ΠϯλʔϑΣʔεͰ ݺͼग़͕͠Մೳ
  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ͷ ܭࢉͰར༻͢Δ ؔ਺ू߹ͷ
 ߏ଄ମ΁ͷࢀর
  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*/ };
  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;
  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;
  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)
  71. Python3 Int • Python3ではlongobjectという名前 • 大体同じだが、digitの要素1の配列を持つ • Cで構造体のメンバに
       可変長配列を設定するテクニック struct

    _longobject { PyObject_VAR_HEAD digit ob_digit[1]; };
  72. typedef struct { OB_VARHEAD object **ob_item; } listobject; extern typeobject

    Listtype; #define is_listobject(op) ((op)->ob_type == &Listtype) • リストの構造体
  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)でアクセス可能
  74. ? • 基本的にこれらの構造体はポインタでcpythonの 内部ではやりとりされる
 (ヒープ領域に常に確保されている) • 構造体はそれぞれ型が異なるが
 いちいち管理するとスタックの扱いがやっかい

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

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

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

    ͜͏ݟΔͱintobject
  78. ? • 必ず構造体の一番上にOB_HEADがあることで
 objectのポインタにキャストする事が出来る

  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);
  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に キャストすると 値が⾒える!!
  81. • Pythonの処理の流れ • コードのおおよその雰囲気 • オブジェクト構造体 • 0.9.1のバグを直す • 計算について

  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 "<stdin>", line 1
  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 "<stdin>", line 1 _⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈_ > 1*1でオーバーフロー!! <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
  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");
  85. Python0.9.1 static object * err_ovf() { err_setstr(OverflowError, "integer overflow"); return

    NULL; } • エラーを文字列として出すだけの関数 • これが呼び出されている部分を探す ➡ intオブジェクトの四則演算で呼びだれている
  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); }
  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
  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
  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
  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
  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
  92. Python0.9.1 x = (double)a * (double)b; if (x > 0x7fffffff

    ||
 x < (double) (long) 0x80000000) return err_ovf(); • 掛け算の結果がintの範囲内に収まっていないと
 err_ovf(オーバーフローを示す例外)の
 関数を返す
  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)
  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の最小値
  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が正解 • バグの原因!!!!!
  96. Python0.9.1 • C言語でプリミティブ型の最大値を見る際は
 型キャストの問題で
 マクロを利用するのが望ましい • limits.hというヘッダーにINT_MAXとINT_MIN がマクロとして定義されているので使用する

  97. Python0.9.1 • 適切にマクロを利用して範囲を見る事で
 バグを修正 #include <limits.h> …(中略)… x = (double)a

    * (double)b; if (x > INT_MAX || x < INT_MIN) return err_ovf();
  98. Python0.9.1 • 掛け算が出来るように!!!!!!!!! +mumei+anatofuz$ src/python >>> print 4*5*-1 -20

  99. Python3 • 基本的にはPython0.9.1と同じコンセプト • 関数ポインタが利用される • オブジェクトごとの掛け算の実装 • オブジェクト2つ取り出して掛け算 •

    本当に? と言われそうなので見ていきます
  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(); }
  101. Python3 ( ) • binary_op1でスロットに登録された演算を実行
 (実際はもう少し色々している) PyObject * PyNumber_Multiply(PyObject *v,

    PyObject *w) { PyObject *result = binary_op1(v, w, NB_SLOT(nb_multiply)); return result; }
  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;
  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;
  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; ֻ͚ࢉͳͲʹରԠͨ͠ ൪߸͕ೖ͍ͬͯΔ
  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͸ม਺͚ͩͲʜ
  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; ؔ਺Έ͍ͨʹݺͼग़͍ͯ͠Δ
  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*/ };
  108. • NB_BINOPは配列に設定されている計算用の
 関数を取り出している (指定をslot番号で行う) #define NB_BINOP(nb_methods, slot) \ (*(binaryfunc*)(& ((char*)nb_methods)[slot]))

  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*/
  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); } }
  111. • Pythonのオブジェクトは大体objectを継承 • キャストして使い、ポインタで基本やりとり • Pythonはバイトコードの形に変換される • 実行される命令はオブジェクトによって異なる • interface的に関数ポインタで制御

    • 大まかな処理はレガシーも同じ!
  112. ( ) • Pythonの歴史にある程度詳しくなりました • Pythonインタプリタがどの様に書かれているか
 ちょっと見てみました • Pytonインタプリタも、結局はCのプログラムな ので、読解してみて自分だけのPythonに改造し

    てみよう!!
  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]))
  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ͷΦϖίʔυ
  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ඪ४ग़ྗʹ ॻ͖ࠐΈ
  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);