use strict; use warnings; use utf8; use lib 'lib'; use Plusen; my $p = Plusen->bootstrap({ config => { meta => { title => "PL_check hacks", author => 'Kazuhiro Osawa - ( Yappo )', email => 'yappo shibuya pl', }, plugins => [ { module => 'ScriptLoader::Simple' }, # { module => 'Device::MozRepl', }, { module => 'Device::Term', }, { module => 'Device::Growl', }, # { module => 'Device::ControlFromFile', config => { path => '/tmp/devsumi-ctl.txt' } }, ], }, }); __DATA__ === title: XSとは === title: CライブラリとPerlを繋げる物 === title: では無くて === title: パッチをあてずにPerlのコアをhackする物である === title: 謝罪 === title: XS厨どころかInternal厨でごめんなさい === title: 謝罪おわり === title: 50%くらいtokuhiromとかぶったな === title: PL_checkとはなんぞや === title: what is PL_check? list: - 関数のフックテーブル - なんのフック? - 構文チェック的な事担当 - BEGINフェーズで実行 - 構文チェック的な事担当 - BEGINフェーズで実行 - 大切な事なので2度言いました === title: 例えばこんなコード書いたら code: | use strict; use warnings; my $x; delete $x; === title: delete argument is not a HASH or ARRAY element or slice === title: と怒られますよね === title: この怒ってる部分がPL_checkで登録された関数が出してる === title: 実装はop.cのPerl_ck_delete(pTHX_ OP *o)を見てね === title: こんなの code: | OP * Perl_ck_delete(pTHX_ OP *o) { o = ck_fun(o); o->op_private = 0; if (o->op_flags & OPf_KIDS) { OP * const kid = cUNOPo->op_first; switch (kid->op_type) { case OP_ASLICE: o->op_flags |= OPf_SPECIAL; /* FALL THROUGH */ === title: つづき code: | case OP_HSLICE: o->op_private |= OPpSLICE; break; case OP_AELEM: o->op_flags |= OPf_SPECIAL; /* FALL THROUGH */ case OP_HELEM: break; === title: つづき(ここでエラー出す) code: | default: Perl_croak(aTHX_ "%s argument is not a HASH or ARRAY element or slice", OP_DESC(o)); } op_null(kid); } return o; } === title: 前ふり終わり、本題に入る === title: tokuhiromの日記見てれば理解できるはずだよ === title: その前にオペコードの構造体の前提知識式だけ === title: OPの定義 see op.h code: | struct op {//超抜粋 /* 次のオペコード */ OP* op_next; /* 兄弟オペコード? ちょっとわからない */ OP* op_sibling; /* 実行フェーズで担当する関数 */ OP* (CPERLscope(*op_ppaddr))(pTHX); /* バイトコード的なコードの種類 */ OPCODE op_type; // 省略 } === title: OPの定義 see perl.h code: | typedef struct op OP; === title: OP は 基本的に OP *o として使うとマクロで扱い易い === title: 何故かというと see op.h code: | #define cUNOPo cUNOPx(o) みたいな定義があるから === title: PL_checkどこで定義されてる? list: - opecode.h に乗ってる - このヘッダファイルには - オペコードに対する関数テーブルもある - PL_ppaddr がそれ - OP で定義された変数の名前は - OP_NAME(OP *o) - OP で定義された変数の説明は - OP_DESC(OP *o) - で見れるよ! === title: 実際の関数はどこに書いてある? list: - わかり易く op.c に書いてある - Perl_ck_ で始まるのがそうだよ - OP *o を受け取ってチェックとかして - 構造体とかいじって、OP *oを返す - この時 o->op_next は存在しない(はず) - なぜなら - BEGINフェーズで準備出来てないから === title: ここ迄の知識さえあればPL_checkを書き換えてautoboxみたいなのが作れる! === title: PL_checkを書き換える事、すなわちPerlのコアに麻薬を打ち込んであり得ない幻覚症状を引き起こす === title: そのためには、どのオペコードがどういう役割を持っているかを熟知してる必要がある === title: 俺は熟知して無い === title: オペコード一覧はperl-users.jpのエキスパートPerlに置いてある === title: http://perl-users.jp/expert_perl/op.html === title: PL_check のついでに ppaddr (PL_ppaddr) について === title: ppaddr概要 list: - オペコード毎に指定されてる - OP で言う所の o->op_ppaddr - 実行フェーズでは、これに登録されてる関数を - オペコードの実際の処理として処理する - 実際は pp.c や pp_*.c に関数定義されてる - OP_ENTERSUB なら PP(pp_entersub) {} な感じ === title: ppaddrのhack list: - OP * ppaddr_hack(pTHX) {} - こんな感じで pTHX 受け取って OP *o 返す - 返す o は次に実行するオペコード - とは言っても、返す o は自分で作んない方がよい - 今実行してるオペコードは PL_op で取る === title: ppaddrまとめ list: - PL_checkはPerlコア全体に影響があるが - ppaddrはhackしても影響範囲が - 特定のオペコードのみという特性がある - 特定のクロージャ関数だけ実行方法を - 変えるhackとかが出来る === title: そういうhackをちょっとやってみてる(まだ動かない><) === title: http://svn.coderepos.org/share/dan/perl/Object-with/branches/yappo/ === title: ppaddrの使い方(超略例) code: | static OP *(*next_bak)(pTHX_ OP *op) = NULL; OP * ppaddr_hack(pTHX) { PerlIO_printf(PerlIO_stderr(), "わーい\n"); return PL_ppaddr[PL_op->op_type](aTHX); } OP * hack_next(pTHX_ OP *o) { o->op_ppaddr = ppaddr_hack; return CALL_FPTR(next_bak)(aTHX_ o); } === title: ppaddrの使い方(超略例)XS部分 code: | BOOT: next_bak = PL_check[OP_NEXT]; PL_check[OP_NEXT] = hack_next; === title: これを使うとPerl の next を実行するたびに「わーい」と表示されてうざくなります === title: === title: 実際のコードを見ながら説明する時間があれば? === title: http://svn.coderepos.org/share/dan/perl/PL_check/trunk/ === title: 以上です、ご清聴ありがとうございました