kilim在JVM上實現了協程,其實現看起來挺容易的:http://www.malhar.net/sriram/kilim/thread_of_ones_own.pdf
在cPython上是否能夠複製其技法呢?粗看上去,是很容易的,甚至比JVM更好實現:
- 利用sys._getframe(0)可以獲得call stack上的任意frame
- frame的f_locals可以獲得這個frame的所有區域性變數的值(https://docs.python.org/3/library/inspect.html)
- 雖然python不支援goto或者longjmp,然後cPython的bytecode是支援JUMP_ABSOLUTE的(https://docs.python.org/3/library/dis.html)
一個更加牛B的事情是,有人已經用@goto裝飾器的方式在Python上實現了GOTO了
這個是Python 2版本的:http://code.activestate.com/recipes/576944-the-goto-decorator/
這個是Python 3版本的:https://github.com/cdjc/goto
這兩個實現很好的展示瞭如何在Python中做類似JVM上ASM庫做的事情,位元組碼增強。
===============
但是,cPython的VM對於frame狀態的建模使得在cPython上實現kilim,比JVM要難得多。在cPython某個指令執行的那一刻,有五個部分的狀態
- builtins
- globals
- locals
- value_stack
- block_stack
前三個都是python裡做動態執行的常客,沒啥困難的。而且frame透過inpsect可以很輕易的獲得locals的值。而後兩者是非常困難的,我們可以來看一段我隨便寫的程式碼
for i in range(1):
print(i)
生成的bytecode是
0 SETUP_LOOP 30 (to 33)
3 LOAD_GLOBAL 0 (0)
6 LOAD_CONST 1 (1)
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 GET_ITER
>> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 0 (0)
19 LOAD_GLOBAL 1 (1)
22 LOAD_FAST 0 (0)
25 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
28 POP_TOP
29 JUMP_ABSOLUTE 13
>> 32 POP_BLOCK
>> 33 LOAD_CONST 0 (0)
36 RETURN_VALUE
我們可以看到一個簡單的迴圈,產生了一對SETUP_LOOP和POP_BLOCK,這個是操作block_stack的。以及GET_ITER和FOR_ITER這是操作value_stack的。如果我們想要直接跳入到print(i)這一行,那麼就要恢復當時的block_stack,以及當時的value_stack。而且要特別注意GET_ITER的含義是把棧頂的值標識為ITER,使得FOR_ITER可以使用,所以還不是簡單地把value_stack恢復就可以了
GET_ITER
Implements TOS = iter(TOS).
這篇部落格把python的frame內部狀態講得非常清楚:http://tech.blog.aknin.name/tag/block-stack/
以上可以解釋為什麼沒有人在python裡搞位元組碼的trick了,因為這個VM太變態。