1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
**************
Bytecode Usage
**************
Installation
============
Install bytecode::
python3 -m pip install bytecode
``bytecode`` requires Python 3.8 or newer.
Hello World
===========
Abstract bytecode
-----------------
Example using abstract bytecode to execute ``print('Hello World!')``::
from bytecode import Instr, Bytecode
bytecode = Bytecode([Instr("LOAD_GLOBAL", (True, 'print')),
Instr("LOAD_CONST", 'Hello World!'),
Instr("CALL", 1),
Instr("POP_TOP"),
Instr("LOAD_CONST", None),
Instr("RETURN_VALUE")])
code = bytecode.to_code()
exec(code)
Output::
Hello World!
Concrete bytecode
-----------------
Example using concrete bytecode to execute ``print('Hello World!')``::
from bytecode import ConcreteInstr, ConcreteBytecode
bytecode = ConcreteBytecode()
bytecode.names = ['print']
bytecode.consts = ['Hello World!', None]
bytecode.extend([ConcreteInstr("LOAD_GLOBAL", 1),
ConcreteInstr("LOAD_CONST", 0),
ConcreteInstr("CALL", 1),
ConcreteInstr("POP_TOP"),
ConcreteInstr("LOAD_CONST", 1),
ConcreteInstr("RETURN_VALUE")])
code = bytecode.to_code()
exec(code)
Output::
Hello World!
Setting the compiler flags
--------------------------
Bytecode, ConcreteBytecode and ControlFlowGraph instances all have a flags
attribute which is an instance of the CompilerFlag enum. The value can be
manipulated like any binary flags.
Setting the OPTIMIZED flag::
from bytecode import Bytecode, CompilerFlags
bytecode = Bytecode()
bytecode.flags |= CompilerFlags.OPTIMIZED
Clearing the OPTIMIZED flag::
from bytecode import Bytecode, CompilerFlags
bytecode = Bytecode()
bytecode.flags ^= CompilerFlags.OPTIMIZED
The flags can be updated based on the instructions stored in the code object
using the method update_flags.
Simple loop
===========
Bytecode of ``for x in (1, 2, 3): print(x)``:
.. tabs::
.. group-tab:: Python >= 3.8
.. code:: python
from bytecode import Label, Instr, Bytecode
loop_start = Label()
loop_done = Label()
loop_exit = Label()
code = Bytecode(
[
# Python 3.8 removed SETUP_LOOP
Instr("LOAD_CONST", (1, 2, 3)),
Instr("GET_ITER"),
loop_start,
Instr("FOR_ITER", loop_exit),
Instr("STORE_NAME", "x"),
Instr("LOAD_GLOBAL", (True, "print")),
Instr("LOAD_NAME", "x"),
Instr("CALL", 1),
Instr("POP_TOP"),
Instr("JUMP_BACKWARD", loop_start),
# Python 3.8 removed the need to manually manage blocks in loops
# This is now handled internally by the interpreter
loop_exit,
Instr("END_FOR"),
Instr("LOAD_CONST", None),
Instr("RETURN_VALUE"),
]
)
# The conversion to Python code object resolve jump targets:
# abstract labels are replaced with concrete offsets
code = code.to_code()
exec(code)
Output::
1
2
3
.. _ex-cond-jump:
Conditional jump
================
Bytecode of the Python code ``print('yes' if test else 'no')``::
from bytecode import Label, Instr, Bytecode
label_else = Label()
label_print = Label()
bytecode = Bytecode([Instr('LOAD_GLOBAL', (True, 'print')),
Instr('LOAD_NAME', 'test'),
Instr('POP_JUMP_IF_FALSE', label_else),
Instr('LOAD_CONST', 'yes'),
Instr('JUMP_FORWARD', label_print),
label_else,
Instr('LOAD_CONST', 'no'),
label_print,
Instr('CALL', 1),
Instr('LOAD_CONST', None),
Instr('RETURN_VALUE')])
code = bytecode.to_code()
test = 0
exec(code)
test = 1
exec(code)
Output::
no
yes
.. note::
Instructions are only indented for readability.
|