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.8k
rakulangで実装する! RubyVM
anatofuz
6
2.1k
沖縄の大学で育った学生がエンジニアになるまで
anatofuz
2
1.5k
OpenAPI Generator Perl Clientでも型チューニングしたい!!
anatofuz
0
270
perlimportsから探るPPIの世界
anatofuz
0
3.5k
GraphQLスキーマの設計で考えたこと
anatofuz
1
1.5k
インターネットアイドル目指して
anatofuz
1
190
How to build traditional Perl interpreters.
anatofuz
0
830
レガシーPerlビルド ~現代に蘇るPerl[1-2].0とPerl6~
anatofuz
2
3.2k
Other Decks in Programming
See All in Programming
リアーキテクチャxDDD 1年間の取り組みと進化
hsawaji
1
220
役立つログに取り組もう
irof
28
9.6k
RubyLSPのマルチバイト文字対応
notfounds
0
120
WebフロントエンドにおけるGraphQL(あるいはバックエンドのAPI)との向き合い方 / #241106_plk_frontend
izumin5210
4
1.4k
LLM生成文章の精度評価自動化とプロンプトチューニングの効率化について
layerx
PRO
2
190
Pinia Colada が実現するスマートな非同期処理
naokihaba
4
220
Arm移行タイムアタック
qnighy
0
300
CSC509 Lecture 09
javiergs
PRO
0
140
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
120
Nurturing OpenJDK distribution: Eclipse Temurin Success History and plan
ivargrimstad
0
870
最新TCAキャッチアップ
0si43
0
140
Why Jakarta EE Matters to Spring - and Vice Versa
ivargrimstad
0
1k
Featured
See All Featured
Navigating Team Friction
lara
183
14k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
131
33k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
250
21k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
47
2.1k
Testing 201, or: Great Expectations
jmmastey
38
7.1k
Code Reviewing Like a Champion
maltzj
520
39k
Measuring & Analyzing Core Web Vitals
bluesmoon
4
120
The Pragmatic Product Professional
lauravandoore
31
6.3k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
Embracing the Ebb and Flow
colly
84
4.5k
10 Git Anti Patterns You Should be Aware of
lemiorhan
654
59k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
27
840
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);