Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
レガシーPython散策
Search
AnaTofuZ
May 18, 2019
Programming
2
1.8k
レガシーPython散策
2019 5/18の #PyConK でのトークです
AnaTofuZ
May 18, 2019
Tweet
Share
More Decks by AnaTofuZ
See All by AnaTofuZ
令和最新版 Perlコーディングガイド
anatofuz
5
4.4k
rakulangで実装する! RubyVM
anatofuz
6
2k
沖縄の大学で育った学生がエンジニアになるまで
anatofuz
2
1.4k
OpenAPI Generator Perl Clientでも型チューニングしたい!!
anatofuz
0
270
perlimportsから探るPPIの世界
anatofuz
0
3.4k
GraphQLスキーマの設計で考えたこと
anatofuz
1
1.5k
インターネットアイドル目指して
anatofuz
1
180
How to build traditional Perl interpreters.
anatofuz
0
820
レガシーPerlビルド ~現代に蘇るPerl[1-2].0とPerl6~
anatofuz
2
3.2k
Other Decks in Programming
See All in Programming
色々なIaCツールを実際に触って比較してみる
iriikeita
0
180
デプロイを任されたので、教わった通りにデプロイしたら障害になった件 ~俺のやらかしを越えてゆけ~
techouse
51
31k
生成 AI を活用した toitta 切片分類機能の裏側 / Inside toitta's AI-Based Factoid Clustering
pokutuna
0
540
/←このスケジュール表に立ち向かう フロントエンド開発戦略 / A front-end development strategy to tackle a single-slash schedule.
nrslib
1
590
弊社の「意識チョット低いアーキテクチャ」10選
texmeijin
4
18k
Googleのテストサイズを活用したテスト環境の構築
toms74209200
0
250
とにかくAWS GameDay!AWSは世界の共通言語!.pdf / Anyway, AWS GameDay! AWS is the world's lingua franca!
seike460
PRO
0
270
Identifying User Idenity
moro
4
6.8k
Honoの来た道とこれから
yusukebe
19
3k
hotwire_or_react
harunatsujita
6
3.5k
Dev ContainersとGitHub Codespacesの素敵な関係
ymd65536
1
120
Vue.js学習の振り返り
hiro_xre
2
130
Featured
See All Featured
How STYLIGHT went responsive
nonsquared
95
5.1k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
46
2.1k
Typedesign – Prime Four
hannesfritz
39
2.4k
GraphQLの誤解/rethinking-graphql
sonatard
66
9.9k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
167
49k
Making the Leap to Tech Lead
cromwellryan
132
8.9k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
32
1.8k
How to Think Like a Performance Engineer
csswizardry
19
1.1k
4 Signs Your Business is Dying
shpigford
180
21k
How To Stay Up To Date on Web Technology
chriscoyier
788
250k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
664
120k
Transcript
レガシー Python 散策 八雲アナグラ @AnaTofuZ 5/18 PyConKyushu 2019
໌ͷʹ ཱͨͳ͍ ࠓ͙͢ߦ͜͏ ڭࣨ
self.inroduce() • 八雲アナグラ (id:AnaTofuZ) • 琉球大学修士1年 • Okinawa.pm(沖縄Perlユーザーグループ)や Perl入学式in沖縄などに出没します •
Pythonは殆ど書きませんが Python製のMercurialをよく使います
• 食後のお昼寝タイム • レガシーPythonを題材に 普段使っているPythonインタプリタが どのようにプログラミングされているかを 探ります • C言語が出てきます
• Pythonとは • Pythonの歴史 • Pythonの初期バージョンの特徴 • C言語での実装に迫る
Python Cで書かれたCpython
Cに変換すれば早いやろ!!!な Cython Python
pythonで書かれたpython pypy Python
Javaで書かれたpython Jython (最近はcafebabepy) Python
python • 処理系が無数に乱立している事もあり スクリプト言語では用語の使い分けを行っている • 大文字の言語「Python」 • プログラミング言語の仕様 • 小文字の言語名「python」
• 処理系の事(基本的にはCで書かれているもの) • それ以外の実装は実装名で呼ぶ
Python • 歴史的に一番古く、一番使われている Cpythonを見ていきます!!! • その為、皆さんがコマンドラインで よく利用している “python” の実装の話です
• Pythonとは • Pythonの歴史 • Pythonの初期バージョン • 動かす • 実装を眺める
• Python3の実装を少し見る
(C)Python • 1991年 0.9x • 最初に公開されたバージョン • 1994年 1.0 • Python初期バージョン
• 2000年 2.0 • Haskellに強い影響を受け出す • 2008年 3.0 • 文字列周りの仕様が大幅に変更される
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
[ ] Perl Python • Perl5のオブジェクト指向では メソッドの第一引数がselfですが、 これはPythonを参考にして実装されている sub client
{ my $self = shift; return $self->{client}; }
CPython • CPython内ではtagで0.97も打たれている ➡ commit ログも見れる ➡ Perlも同じようなリポジトリの運用 • CPythonをcloneすると大体のバージョンの
ソースコードがローカルで見る事が出来る
Python • 微妙にリポジトリに全てのバージョンが 残っていない • 手軽に試せるのは公式のwebpageから ダウンロードできる0.9.1 https://www.python.org/download/releases/ early/
Python0.9.1 • configureではなくMakefileがあり HighSierraだと普通にmakeできる • 検索するといくつか触っている人がいるので 幾つかのブログで紹介されている • 公式ドキュメントを参照する場合は リポジトリに含まれているものを利用する
Python0.9.1 • manページ • TeXで書かれたドキュメント • コアライブラリ • testall.py
Python0.9.1 • manページ ➡ ほとんど役に立たない • TeXで書かれたドキュメント ➡ 結構書かれている •
コアライブラリ ➡ 複雑なプログラミングの参考になる • testall.py ➡ 使い方の参考になる
Python • LaTeXで書かれている (書き方が古いらしい) • Makefileがあり、makeを実行すると xdvi (x window のdviビューワー)が起動する
• xdviの機能でPostScriptに出力する事が可能 • 全23ページ
Python0.9.1 • あるもの • Intなどのデータ型 • リスト、dict • dictは開発中で、構文には組み込まれていない •
オブジェクトシステム、継承 • 今のPythonとはかなり異なる • import機能 • コアライブラリがついていた
Python0.9.1 • ないもの • タプル • map, lambda, filter •
version1からLispの影響で導入 • リスト内包記法 • version2からHaskellの影響で導入 • ジェネレーター, withなど
コードを書く (我々) 字句解析 構⽂解析 バイナリファイル ⽣成 バイナリファイル を実⾏
Python コードを書く (我々) 字句解析 構⽂解析 バイトコード⽣成 バイトコードを VMが実⾏
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
Python0.9.1 • dicやタプルなど、実装しているが構文に組み込 まれておらず使えない機能が多い • 文字列は “hoge” ではだめで ‘hoge’ なシングルクォートしか使えない
Python ୈҰ෦ऴ ─── ੍࡞ɾஶ࡞ N◦K
$ݴޠͰͷ ࣮ʹ ഭ͍͖ͬͯ·͢
• Pythonの処理の流れ • コードのおおよその雰囲気 • オブジェクト構造体 • 0.9.1のバグを直す • 計算について
Python • ソースコードをバイトコード化し VMが実行する流れは0.9.1から3まで同じ • 最適化やAPI、計算方法などには違いがあるが 大まかな枠組みは既に形成されている • バイトコードを確認するdisモジュールが 既に0.9.1から付随している
CPython • Grammarファイルに記述されている • EBNF(拡張バッカスナウア記法)っぽいもので定義 • PythonはMake時に、このファイルから 決定性有限オートマトンを自動生成し、それを利用する • Python公式の0.971は既にGrammarが
ビルドされている為、自動生成はされない
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)) )
• 構文解析の結果から扱いやすい バイトコード形式に内部的に変換する ➡ 速度向上と実装の分離の為 • 生成したバイトコードは、計算機をエミュレー トするプロセスVMで実行される
PythonVM • スタックマシンと呼ばれるVMの作り方 • 大きいスタックで値をやりとりする • RubyのYARVも同じ方式 • 実際のCPUに似せている方式はレジスタマシン •
Perl6はこの方式
• バイトコードは実行する命令(オペコード)と 引数(オペランド)や、データの集まり • オペコードは数値に変換される • CPUのプログラムカウンタ的に、バイトコード を進めながらプログラムを実行していく
Python ΦϖίʔυಡΈࠐΈ ໋ྩ࣮ߦ ҾಡΈࠐΈ
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; ΦϖίʔυΛ औΓग़͠
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; Ҿ͕͋ΕҾΛ औΓग़͠
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จͰ ໋ྩͷॲཧʹذ
PythonVM case BINARY_ADD: w = POP(); v = POP(); x
= add(v, w); DECREF(v); DECREF(w); PUSH(x); break; ελοΫ͔Β ͭͷมΛऔΓग़͠ w v
PythonVM case BINARY_ADD: w = POP(); v = POP(); x
= add(v, w); DECREF(v); DECREF(w); PUSH(x); break; ͭΛͯ͠YΛ࡞
PythonVM case BINARY_ADD: w = POP(); v = POP(); x
= add(v, w); DECREF(v); DECREF(w); PUSH(x); break; WͱXͷϦϑΝϨϯε ΧϯτΛ σΫϦϝϯτ͢Δ
PythonVM case BINARY_ADD: w = POP(); v = POP(); x
= add(v, w); DECREF(v); DECREF(w); PUSH(x); break; YΛελοΫʹ͍Εͯऴྃ x
Python3 • Python3では、いちいちループすると遅いので GCCの拡張機能のラベルgotoなどを利用し スピードの高速化を図っている • 他のスクリプト言語も大体同じような実装なの で、興味があれば他の言語も見てみましょう
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*/ }
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$Ͱؔએݴ
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]; ҾΛϑΝΠϧ໊ʹ ઃఆ
Python main initall(); setpythonpath(getpythonpath()); setpythonargv(argc-1, argv+1); goaway(run(fp, filename == NULL
? "<stdin>" : filename) /*NOTREACHED*/ ࣮ଶSVOͰ HPBXBZͰΤϥʔͷ ย͚
Python • Pythonでは様々なオブジェクトが登場します • C言語の世界では、これらオブジェクトは 主に構造体として定義しています (構造体 = 複数の型と名前を持つもの) •
Python3では基本的にPyObjectと呼ばれるもの から派生します
Python • メモリのヒープ領域に必ず置かれる • スタックや静的領域には置かれない • その為メモリの中でオブジェクトの アドレスが変更される事は無い CODE DATA
CONST DATA ... HEAP ... STACK ...
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;
varobject • objectを拡張したvarobject構造体もある • PyVarObjectに対応する • サイズの概念が追加される #define OB_VARHEAD \
OB_HEAD \ unsigned int ob_size; /* Number of items in variable part */ typedef struct { OB_VARHEAD } varobject;
object ob_refcnt ob_type
object ob_refcnt ob_type ΨϕʔδίϨΫλ ϦϑΝϨϯεΧϯτͷ Χϯλʔ Ͳ͔͜Βࢀর͞Ε ͳ͘ͳΔ ʹͳΔ ͱ
ղ์͞ΕΔ
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))
object ob_refcnt ob_type
object ob_refcnt ob_type ΦϒδΣΫτͷ ܕใͷ ϙΠϯλ
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;
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 *)); ܕͷ໊લใ
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 *)); ΦϒδΣΫτͷղ์ʹ ར༻ͨؔ͠
typeobject • void* なこれは、関数ポインタ • 関数の参照を代入する事が出来る • 構造体に関数ポインタをいれることで インターフェース的な機能を実現 •
FPROTOはマクロで、プロトタイプ宣言を抑制 • 当時のコンパイラはプロトタイプ宣言があると 解釈出来ないケースもあり、マクロで抑制 (プロトタイプ宣言…Cで関数定義時に必要) void (*tp_dealloc) FPROTO((object *));
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*/ };
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*/ };
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ʹొ ΦϒδΣΫτ͝ͱʹ ҟͳΔؔΛ ಉ͡ΠϯλʔϑΣʔεͰ ݺͼग़͕͠Մೳ
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ͷ ܭࢉͰར༻͢Δ ؔू߹ͷ ߏମͷࢀর
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*/ };
( )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;
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;
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)
Python3 Int • Python3ではlongobjectという名前 • 大体同じだが、digitの要素1の配列を持つ • Cで構造体のメンバに 可変長配列を設定するテクニック struct
_longobject { PyObject_VAR_HEAD digit ob_digit[1]; };
typedef struct { OB_VARHEAD object **ob_item; } listobject; extern typeobject
Listtype; #define is_listobject(op) ((op)->ob_type == &Listtype) • リストの構造体
typedef struct { OB_VARHEAD object **ob_item; } listobject; extern typeobject
Listtype; #define is_listobject(op) ((op)->ob_type == &Listtype) ΦϒδΣΫτͷࢀরྻ ϙΠϯλͷϙΠϯλ • ob_itemはC言語の配列になるので O(1)でアクセス可能
? • 基本的にこれらの構造体はポインタでcpythonの 内部ではやりとりされる (ヒープ領域に常に確保されている) • 構造体はそれぞれ型が異なるが いちいち管理するとスタックの扱いがやっかい
IntObject • objectとintObjectの違いはob_ivalの有無 typedef struct { OB_HEAD long ob_ival; }
intobject; typedef struct _object { OB_HEAD } object;
IntObject typedef struct { OB_HEAD long ob_ival; } intobject; ͜͏ݟΔͱobject
IntObject typedef struct { OB_HEAD long ob_ival; } intobject; ͜͏ݟΔͱobject
͜͏ݟΔͱintobject
? • 必ず構造体の一番上にOB_HEADがあることで objectのポインタにキャストする事が出来る
>>> 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);
>>> 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に キャストすると 値が⾒える!!
• Pythonの処理の流れ • コードのおおよその雰囲気 • オブジェクト構造体 • 0.9.1のバグを直す • 計算について
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
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^ ̄
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");
Python0.9.1 static object * err_ovf() { err_setstr(OverflowError, "integer overflow"); return
NULL; } • エラーを文字列として出すだけの関数 • これが呼び出されている部分を探す ➡ intオブジェクトの四則演算で呼びだれている
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); }
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
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
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
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
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
Python0.9.1 x = (double)a * (double)b; if (x > 0x7fffffff
|| x < (double) (long) 0x80000000) return err_ovf(); • 掛け算の結果がintの範囲内に収まっていないと err_ovf(オーバーフローを示す例外)の 関数を返す
Python0.9.1 x = (double)a * (double)b; if (x > 0x7fffffff
|| x < (double) (long) 0x80000000) return err_ovf(); • 0x7fffffffが32bitのintの最大値 (2^31 -1)
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の最小値
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が正解 • バグの原因!!!!!
Python0.9.1 • C言語でプリミティブ型の最大値を見る際は 型キャストの問題で マクロを利用するのが望ましい • limits.hというヘッダーにINT_MAXとINT_MIN がマクロとして定義されているので使用する
Python0.9.1 • 適切にマクロを利用して範囲を見る事で バグを修正 #include <limits.h> …(中略)… x = (double)a
* (double)b; if (x > INT_MAX || x < INT_MIN) return err_ovf();
Python0.9.1 • 掛け算が出来るように!!!!!!!!! +mumei+anatofuz$ src/python >>> print 4*5*-1 -20
Python3 • 基本的にはPython0.9.1と同じコンセプト • 関数ポインタが利用される • オブジェクトごとの掛け算の実装 • オブジェクト2つ取り出して掛け算 •
本当に? と言われそうなので見ていきます
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(); }
Python3 ( ) • binary_op1でスロットに登録された演算を実行 (実際はもう少し色々している) PyObject * PyNumber_Multiply(PyObject *v,
PyObject *w) { PyObject *result = binary_op1(v, w, NB_SLOT(nb_multiply)); return result; }
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;
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;
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; ֻ͚ࢉͳͲʹରԠͨ͠ ൪߸͕ೖ͍ͬͯΔ
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ม͚ͩͲʜ
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; ؔΈ͍ͨʹݺͼग़͍ͯ͠Δ
• そういえば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*/ };
• NB_BINOPは配列に設定されている計算用の 関数を取り出している (指定をslot番号で行う) #define NB_BINOP(nb_methods, slot) \ (*(binaryfunc*)(& ((char*)nb_methods)[slot]))
• 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*/
• 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); } }
• Pythonのオブジェクトは大体objectを継承 • キャストして使い、ポインタで基本やりとり • Pythonはバイトコードの形に変換される • 実行される命令はオブジェクトによって異なる • interface的に関数ポインタで制御
• 大まかな処理はレガシーも同じ!
( ) • Pythonの歴史にある程度詳しくなりました • Pythonインタプリタがどの様に書かれているか ちょっと見てみました • Pytonインタプリタも、結局はCのプログラムな ので、読解してみて自分だけのPythonに改造し
てみよう!!
• 基本的に正の数で表現されている為 負の数は、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]))
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ͷΦϖίʔυ
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ඪ४ग़ྗʹ ॻ͖ࠐΈ
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);