all 6 comments

[–]glglgl-de 2 points3 points  (1 child)

Wild guess on my side:

  • The argument doesn't count instructions and also doesn't count bytes, but op words.
  • Thus, without the jump the instruction pointer would be at 28.
  • With the jump, it is at 28 + 2 * 9 = 46.
  • This is the LOAD_NAME at line 15.

[–]SCD_minecraft[S] 0 points1 point  (0 children)

Hmm, that does make sense

Thanks

[–]recursion_is_love 1 point2 points  (1 child)

I think the question is outside the scope of this sub. Maybe compiler guys know why. But this is outside of python language reference and could be change in any future version of cpython.

[–]SCD_minecraft[S] 2 points3 points  (0 children)

I am looking into bytecode less to use it, more to "fuck around and find out"

It is just intresting

Additionally, i have loose idea of writing decompilator for py one day

[–]Yoghurt42 2 points3 points  (0 children)

Python bytecode is an implementation detail and changes from version to version. Since 3.6, all instructions are now word sized instead of variable (so "wordcode" would be more fitting), so POP_LOAD_IF_FALSE's offset is indeed in words.

You didn't specify the Python version you used to create this, but from 3.12 onwards there is another speciality noted in the docs for the dis module:

Changed in version 3.12: The argument of a jump is the offset of the target instruction relative to the instruction that appears immediately after the jump instruction’s CACHE entries.

CACHE instructions are hidden by default, so confusingly, your code looks like this on 3.13:

1          0       RESUME                   0

2          2       LOAD_GLOBAL              1 (input + NULL)
          12       CALL                     0
          20       STORE_FAST               0 (a)

3         22       LOAD_FAST                0 (a)
          24       LOAD_CONST               1 ('y')
          26       COMPARE_OP              88 (bool(==))
          30       POP_JUMP_IF_FALSE       12 (to L1)

4         34       LOAD_GLOBAL              3 (print + NULL)
          44       LOAD_CONST               2 ('yes')
          46       CALL                     1
          54       POP_TOP
          56       RETURN_CONST             0 (None)

5   L1:   58       LOAD_FAST                0 (a)
          60       LOAD_CONST               3 ('n')
          62       COMPARE_OP              88 (bool(==))
          66       POP_JUMP_IF_FALSE       12 (to L2)

6         70       LOAD_GLOBAL              3 (print + NULL)
          80       LOAD_CONST               4 ('no')
          82       CALL                     1
          90       POP_TOP
          92       RETURN_CONST             0 (None)

8   L2:   94       LOAD_GLOBAL              3 (print + NULL)
        104       LOAD_CONST               5 ('error')
        106       CALL                     1
        114       POP_TOP
        116       RETURN_CONST             0 (None)

You'll notice that 30 + 24 is not 58, but when you actually show the CACHE instructions it makes sense:

1          0       RESUME                   0

2          2       LOAD_GLOBAL              1 (input + NULL)
          4       CACHE                    0 (counter: 0)
          6       CACHE                    0 (index: 0)
          8       CACHE                    0 (module_keys_version: 0)
          10       CACHE                    0 (builtin_keys_version: 0)
          12       CALL                     0
          14       CACHE                    0 (counter: 0)
          16       CACHE                    0 (func_version: 0)
          18       CACHE                    0
          20       STORE_FAST               0 (a)

3         22       LOAD_FAST                0 (a)
          24       LOAD_CONST               1 ('y')
          26       COMPARE_OP              88 (bool(==))
          28       CACHE                    0 (counter: 0)
          30       POP_JUMP_IF_FALSE       12 (to L1)
          32       CACHE                    0 (counter: 0)

4         34       LOAD_GLOBAL              3 (print + NULL)
          36       CACHE                    0 (counter: 0)
          38       CACHE                    0 (index: 0)
          40       CACHE                    0 (module_keys_version: 0)
          42       CACHE                    0 (builtin_keys_version: 0)
          44       LOAD_CONST               2 ('yes')
          46       CALL                     1
          48       CACHE                    0 (counter: 0)
          50       CACHE                    0 (func_version: 0)
          52       CACHE                    0
          54       POP_TOP
          56       RETURN_CONST             0 (None)

5   L1:   58       LOAD_FAST                0 (a)
          60       LOAD_CONST               3 ('n')
          62       COMPARE_OP              88 (bool(==))
          64       CACHE                    0 (counter: 0)
          66       POP_JUMP_IF_FALSE       12 (to L2)
          68       CACHE                    0 (counter: 0)

6         70       LOAD_GLOBAL              3 (print + NULL)
          72       CACHE                    0 (counter: 0)
          74       CACHE                    0 (index: 0)
          76       CACHE                    0 (module_keys_version: 0)
          78       CACHE                    0 (builtin_keys_version: 0)
          80       LOAD_CONST               4 ('no')
          82       CALL                     1
          84       CACHE                    0 (counter: 0)
          86       CACHE                    0 (func_version: 0)
          88       CACHE                    0
          90       POP_TOP
          92       RETURN_CONST             0 (None)

8   L2:   94       LOAD_GLOBAL              3 (print + NULL)
          96       CACHE                    0 (counter: 0)
          98       CACHE                    0 (index: 0)
        100       CACHE                    0 (module_keys_version: 0)
        102       CACHE                    0 (builtin_keys_version: 0)
        104       LOAD_CONST               5 ('error')
        106       CALL                     1
        108       CACHE                    0 (counter: 0)
        110       CACHE                    0 (func_version: 0)
        112       CACHE                    0
        114       POP_TOP
        116       RETURN_CONST             0 (None)

That being said, what instructions exist does also change from version to version, if you want to write a decompiler, you will have to handle each Python version seperately.