---
# System prepended metadata

title: Python 3.9 rules
tags: [decompiler]

---

---
tags: decompiler
title: Python 3.9 rules
---

# List of opcodes that differ



| OP CODE py3.9 | OP Name py3.9 | 
| -------- | -------- | 
| `<121>`     | `JUMP_IF_NOT_EXC_MATCH`    | 
| `<48>`    | `RERAISE`|
| `<117>`   | `IS_OP` |
| `<118>`   | `CONTAINS_OP` |
| `<165>`   | `DICT_MERGE` |
| `<74>`    | `LOAD_ASSERTION_ERROR` |
| `<165>`   | `DICT_UPDATE` |
Alot of these are derivatives of `COMPARE_OP` in py3.8. The arguments of `COMPARE_OP` are as follows:
```python=
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
        'is not', 'exception match', 'BAD')
# Where each index maps to the argument for COMPARE_OP
```

# Implementation logs

- Update arguments of jump instructions not in python 3.8 that are present in python 3.9. Example: 121 does not exist in python 3.8 but needs to be updated when instructions are added or removed from bytecode. 

# Transformations for cross-compilations

## 1. <121> - Replace with `[COMPARE_OP][POP_JUMP_IF_FALSE]`

Pattern: `[<121>]` 
Replace with: `[COMPARE_OP][POP_JUMP_IF_FALSE]`

- Where `POP_JUMP_IF_FALSE` will jump to the same offset as `<121>`. 
- `COMPARE_OP` will have argument of `10` for exception matching.


## 2. `<48>` - Replace with `END_FINALLY`
Pattern: `[<48>]` 
Replace with: `[END_FINALLY]`

## 3. `<48><48>` - Replace with `END_FINALLY`, `POP_EXCEPT` `JUMP_FORWARD` `END_FINALLY` (not implemented) - NOT ALWAYS TRUE.

Pattern: `[POP_EXCEPT].+[JUMP_FORWARD].+[<48>][<48>]` 
Replace with: `[BEGIN_FINALLY].+[END_FINALLY][POP_EXCEPT][JUMP_FORWARD][END_FINALLY]`

- This caters to nested try/except
- connect the two `END_FINALLY` with `POP_EXCEPT` and `JUMP_FORWARD`

Another one:

Pattern: `[SETUP_FINALLY].*[POP_BLOCK][POP_EXCEPT].*[JUMP_FORWARD].*[<48>].*[<48>]`
Transform to: `[SETUP_FINALL].*[POP_BLOCK][BEGIN_FINALLY].*[END_FINALLY][POP_EXCEPT][JUMP_FORWARD][END_FINALLY]`
![](https://i.imgur.com/APRpeqM.png)

Example:
```
try:
    key = kwargs.pop("key")
except KeyError as e:
    raise TypeError("Missing parameter:" + str(e))
```

## 4. `<117>` - Replace with `COMPARE_OP`

Pattern: `[<117>]` 
Replace with: `[COMPARE_OP]`

- Where `117` is equivalent to `is` operator and `is not` if the argument is `1`.
- Convert it to `COMPARE_OP` with argument `8` or `9` given `117` has argument `0` or `1` respectively.

## 5. `<118>` - Replace with `COMPARE_OP`

Pattern: `[<118>]` 
Replace with: `[COMPARE_OP]`

- Where `118` is equivalent to `in` operator and `not in` if the argument is `1`.
- Convert it to `COMPARE_OP` with argument `6` or `7` given `117` has argument `0` or `1` respectively.

## 6. `<164>` - Replace with `BUILD_MAP_UNPACK_WITH_CALL`

Pattern: `[<164>]`
Replace with: `[BUILD_MAP_UNPACK_WITH_CALL]`

## 7. `<165>` - Replace with `BUILD_MAP_UNPACK` (not implemented)

Pattern: `[BUILD_MAP].+[<165>].+[BUILD_CONST_KEY_MAP][<165>]`
Replace with: `.+[BUILD_CONST_KEY_MAP][BUILD_MAP_UNPACK]`

- Remove `BUILD_MAP` and `<165>` before `BUILD_CONST_KEY_MAP`.
- Replace `[<165>]` after `BUILD_CONST_KEY_MAP` with `BUILD_MAP_UNPACK`

## 8. `<74>` - Replace with `LOAD_GLOBAL`

Pattern: `[<74>]`
Replace with: `[LOAD_GLOBAL]`

- Replace with load global and add `assertion error` to co_names

## 9. `with` handling

Pattern: `[SETUP_WITH].+[POP_BLOCK].*[<49>][POP_JUMP_IF_TRUE][END_FINALLY]`
Replace with: `[SETUP_WITH].+[POP_BLOCK][BEGIN_FINALLY][WITH_CLEANUP_START][WITH_CLEANUP_FINISH][END_FINALLY]`

Transformation
- Remove all Instructions between `[POP_BLOCK]` and `[END_FINALLY]`
- Add instructions `[BEGIN_FINALLY][WITH_CLEANUP_START][WITH_CLEANUP_FINISH]` between `[POP_BLOCK]` and `[END_FINALLY]`

## 10. Handle `try/finally` - no `except`

Pattern: `[SETUP_FINALLY].*[POP_BLOCK].*[JUMP_FORWARD].*[<48>]`
Replace with: `[SETUP_FINALLY].*[POP_BLOCK][BEGIN_FINALLY].*[END_FINALLY]`

Transformation: Just replace `.*[JUMP_FORWARD]` with `[BEGIN_FINALLY]`

## 11. variable arguments in function invocations

Pattern: `[LOAD_GLOBAL, LOAD_FAST].*[BUILD_LIST][LOAD_FAST].*[<164>]?[CALL_FUNCTION_EX]`
Transofrm: `[LOAD_GLOBAL, LOAD_FAST].*[BUILD_TUPLE][LOAD_FAST][BUILD_TUPLE_UNPACK_WITH_CALL].*[CALL_FUNCTOPN_EX]`
![](https://i.imgur.com/PPhP3Fl.png)

Example:
```
func_call = functools.partial(ctx.run, func, *args, **kwargs)
```

## 12. Building list:

Pattern: `[BUILD_LIST][LOAD_CONST][CALL_FINALLY]`
Transform: `[LOAD_CONST]+[BUILD_LIST]`

## 13. Building set:

Pattern: `[BUILD_SET][LOAD_CONST][POP_FINALLY][STORE_NAME]?`
Transform: `[LOAD_CONST]+[BUILD_SET]`

:::info
Note that the build set and build list both are flattened in python 3.8. In python 3.8 each element is loaded seperately while in python 3.9 all elements are loaded in a single tuple.
:::

