# 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_NAME", 'print'),
                         Instr("LOAD_CONST", 'Hello World!'),
                         Instr("CALL_FUNCTION", 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_NAME", 0),
                     ConcreteInstr("LOAD_CONST", 0),
                     ConcreteInstr("CALL_FUNCTION", 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)`:

    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_NAME", "print"),
                Instr("LOAD_NAME", "x"),
                Instr("CALL_FUNCTION", 1),
                Instr("POP_TOP"),
                Instr("JUMP_ABSOLUTE", 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("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

## 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_NAME', '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_FUNCTION', 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.