I have been using 'dis' module in order to re-write some compiled script (.pyc). I understand the difference between JUMP_FORWARD and JUMP_ABSOLUTE. To my knowledge an IF statement will be closed by a JUMP_FORWARD:
>>> def f():
if a:
print ''
>>> from dis import dis
>>> dis(f)
2 0 LOAD_GLOBAL 0 (a)
3 JUMP_IF_FALSE 9 (to 15)
6 POP_TOP
3 7 LOAD_CONST 1 ('')
10 PRINT_ITEM
11 PRINT_NEWLINE
12 JUMP_FORWARD 1 (to 16)
>> 15 POP_TOP
>> 16 LOAD_CONST 0 (None)
19 RETURN_VALUE
And the JUMP_ABSOLUTE will appear if the IF statement is at the end of another loop. For instance:
>>> def f1():
if a:
if b:
print ''
>>> dis(f1)
2 0 LOAD_GLOBAL 0 (a)
3 JUMP_IF_FALSE 20 (to 26)
6 POP_TOP
3 7 LOAD_GLOBAL 1 (b)
10 JUMP_IF_FALSE 9 (to 22)
13 POP_TOP
4 14 LOAD_CONST 1 ('')
17 PRINT_ITEM
18 PRINT_NEWLINE
19 JUMP_ABSOLUTE 27
>> 22 POP_TOP
23 JUMP_FORWARD 1 (to 27)
>> 26 POP_TOP
>> 27 LOAD_CONST 0 (None)
30 RETURN_VALUE
From the Bytecode I am reading to write back the code, there is a JUMP_ABSOLUTE that surprises me:
121 228 LOAD_FAST 11 (a)
231 LOAD_CONST 9 (100)
234 COMPARE_OP 0 (<)
237 JUMP_IF_FALSE 23 (to 263)
240 POP_TOP
241 LOAD_FAST 11 (b)
244 LOAD_CONST 11 (10)
247 COMPARE_OP 4 (>)
250 JUMP_IF_FALSE 10 (to 263)
253 POP_TOP
122 254 LOAD_CONST 3 (1)
257 STORE_FAST 4 (ok)
260 JUMP_ABSOLUTE 27
>> 263 POP_TOP
I would think the code is the following:
if a<100 and b>10:
ok=1
but it provokes a JUMP_FORWARD and not a JUMP_ABSOLUTE. I know it is not a WHILE loop, nor a FOR statement because they both create a SETUP_LOOP line in the Bytecode.
My question is: what am I missing ? why do I get a FORWARD instead of ABSOLUTE jump ?
EDIT: The absolute jump to index 27 points to the beginning of a (WHILE?) loop in which these two lines 121 and 122 belong to:
106 24 SETUP_LOOP 297 (to 324)
>> 27 LOAD_FAST 4 (ok)
30 LOAD_CONST 1 (0)
33 COMPARE_OP 2 (==)
36 JUMP_IF_FALSE 283 (to 322)
39 POP_TOP
There is an IF-statement before and another one after these lines. Here is the code before, with the same JUMP_ABSOLUTE closing the statement.
115 170 LOAD_FAST 3 (q)
173 LOAD_CONST 10 (1)
176 COMPARE_OP 0 (<)
179 JUMP_IF_FALSE 45 (to 227)
182 POP_TOP
183 LOAD_FAST 11 (z)
186 LOAD_CONST 11 (10)
189 COMPARE_OP 4 (>)
192 JUMP_IF_FALSE 32 (to 227)
195 POP_TOP
116 196 LOAD_CONST 1 (0)
199 STORE_FAST 4 (ok)
117 202 LOAD_FAST 5 (u)
205 LOAD_CONST 3 (1)
208 BINARY_ADD
209 STORE_FAST 5 (u)
118 212 LOAD_CONST 1 (0)
215 STORE_FAST 3 (k)
119 218 LOAD_CONST 3 (10)
221 STORE_FAST 6 (dv)
224 JUMP_ABSOLUTE 27
>> 227 POP_TOP
The JUMP_FORWARD says "go to the next line" and the JUMP_ABSOLUTE says "go back to the beginning of the WHILE loop". Problem is I don't know how to replicate a code that would give the same bytecode as above.
Thank you !
I took the challenge and with your help was able to reproduce your situation (or something very similar) using the following (nonsense) function:
>>> def f():
... while ok==0:
... if q<1 and z>10:
... ok=0
... u=u+1
... k=0
... dv=10
... elif a<100 and b>10:
... ok=1
...
>>> dis(f)
2 0 SETUP_LOOP 112 (to 115)
>> 3 LOAD_FAST 0 (ok)
6 LOAD_CONST 1 (0)
9 COMPARE_OP 2 (==)
12 JUMP_IF_FALSE 98 (to 113)
15 POP_TOP
3 16 LOAD_GLOBAL 0 (q)
19 LOAD_CONST 2 (1)
22 COMPARE_OP 0 (<)
25 JUMP_IF_FALSE 45 (to 73)
28 POP_TOP
29 LOAD_GLOBAL 1 (z)
32 LOAD_CONST 3 (10)
35 COMPARE_OP 4 (>)
38 JUMP_IF_FALSE 32 (to 73)
41 POP_TOP
4 42 LOAD_CONST 1 (0)
45 STORE_FAST 0 (ok)
5 48 LOAD_FAST 1 (u)
51 LOAD_CONST 2 (1)
54 BINARY_ADD
55 STORE_FAST 1 (u)
6 58 LOAD_CONST 1 (0)
61 STORE_FAST 2 (k)
7 64 LOAD_CONST 3 (10)
67 STORE_FAST 3 (dv)
70 JUMP_ABSOLUTE 3
>> 73 POP_TOP
8 74 LOAD_GLOBAL 2 (a)
77 LOAD_CONST 4 (100)
80 COMPARE_OP 0 (<)
83 JUMP_IF_FALSE 23 (to 109)
86 POP_TOP
87 LOAD_GLOBAL 3 (b)
90 LOAD_CONST 3 (10)
93 COMPARE_OP 4 (>)
96 JUMP_IF_FALSE 10 (to 109)
99 POP_TOP
9 100 LOAD_CONST 2 (1)
103 STORE_FAST 0 (ok)
106 JUMP_ABSOLUTE 3
>> 109 POP_TOP
110 JUMP_ABSOLUTE 3
>> 113 POP_TOP
114 POP_BLOCK
>> 115 LOAD_CONST 0 (None)
118 RETURN_VALUE
Lines 8 and 11 have the JUMP_ABSOLUTE that you were asking for. Slight differences like LOAD_GLOBAL versus LOAD_FAST are caused by the scope of the variables.
Note that I had to switch to Python 2.5 to reproduce this. Newer versions produce different results.
If continue does not seem to be applicable to your situation, I suggest you do some research in the source code of Python and look for ADDOP_JABS in Python/compile.c to find out in what other cases an absolute jump is inserted.
If your goal is to "just" decompile this .pyc, then you should try uncompyle2, which describes itself as "A Python 2.5, 2.6, 2.7 byte-code decompiler, written in Python 2.7"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With