@@ -50,6 +50,7 @@ class StackValue(NamedTuple):
50
50
51
51
_MIN_PY311 = sys .version_info >= (3 , 11 )
52
52
_MIN_PY312 = _MIN_PY311 and sys .version_info >= (3 , 12 )
53
+ _MIN_PY314 = _MIN_PY312 and sys .version_info >= (3 , 14 )
53
54
54
55
55
56
class OpNames :
@@ -87,6 +88,10 @@ class OpNames:
87
88
LOAD_VALUES = frozenset (("LOAD_CONST" , "LOAD_DEREF" , "LOAD_FAST" , "LOAD_GLOBAL" ))
88
89
LOAD_ATTR = frozenset ({"LOAD_METHOD" , "LOAD_ATTR" })
89
90
LOAD = LOAD_VALUES | LOAD_ATTR
91
+ SIMPLIFY_SPECIALIZED : ClassVar [dict [str , str ]] = {
92
+ "LOAD_FAST_BORROW" : "LOAD_FAST" ,
93
+ "LOAD_SMALL_INT" : "LOAD_CONST" ,
94
+ }
90
95
SYNTHETIC : ClassVar [dict [str , int ]] = {
91
96
"POLARS_EXPRESSION" : 1 ,
92
97
}
@@ -102,7 +107,9 @@ class OpNames:
102
107
| set (SYNTHETIC )
103
108
| LOAD_VALUES
104
109
)
105
- MATCHABLE_OPS = PARSEABLE_OPS | set (BINARY ) | LOAD_ATTR | CALL
110
+ MATCHABLE_OPS = (
111
+ set (SIMPLIFY_SPECIALIZED ) | PARSEABLE_OPS | set (BINARY ) | LOAD_ATTR | CALL
112
+ )
106
113
UNARY_VALUES = frozenset (UNARY .values ())
107
114
108
115
@@ -736,6 +743,7 @@ class RewrittenInstructions:
736
743
[
737
744
"COPY" ,
738
745
"COPY_FREE_VARS" ,
746
+ "NOT_TAKEN" ,
739
747
"POP_TOP" ,
740
748
"PRECALL" ,
741
749
"PUSH_NULL" ,
@@ -762,7 +770,7 @@ def __init__(
762
770
if inst .opname not in OpNames .MATCHABLE_OPS :
763
771
self ._rewritten_instructions = []
764
772
return
765
- upgraded_inst = self ._upgrade_instruction (inst )
773
+ upgraded_inst = self ._update_instruction (inst )
766
774
normalised_instructions .append (upgraded_inst )
767
775
768
776
self ._rewritten_instructions = self ._rewrite (normalised_instructions )
@@ -1077,7 +1085,10 @@ def _unpack_superinstructions(
1077
1085
) -> Iterator [Instruction ]:
1078
1086
"""Expand known 'superinstructions' into their component parts."""
1079
1087
for inst in instructions :
1080
- if inst .opname == "LOAD_FAST_LOAD_FAST" :
1088
+ if inst .opname in (
1089
+ "LOAD_FAST_LOAD_FAST" ,
1090
+ "LOAD_FAST_BORROW_LOAD_FAST_BORROW" ,
1091
+ ):
1081
1092
for idx in (0 , 1 ):
1082
1093
yield inst ._replace (
1083
1094
opname = "LOAD_FAST" ,
@@ -1088,13 +1099,29 @@ def _unpack_superinstructions(
1088
1099
yield inst
1089
1100
1090
1101
@staticmethod
1091
- def _upgrade_instruction (inst : Instruction ) -> Instruction :
1092
- """Rewrite any older binary opcodes using py 3.11 'BINARY_OP' instead ."""
1102
+ def _update_instruction (inst : Instruction ) -> Instruction :
1103
+ """Update/modify specific instructions to simplify multi-version parsing ."""
1093
1104
if not _MIN_PY311 and inst .opname in OpNames .BINARY :
1105
+ # update older binary opcodes using py >= 3.11 'BINARY_OP' instead
1094
1106
inst = inst ._replace (
1095
1107
argrepr = OpNames .BINARY [inst .opname ],
1096
1108
opname = "BINARY_OP" ,
1097
1109
)
1110
+ elif _MIN_PY314 :
1111
+ if (opname := inst .opname ) in OpNames .SIMPLIFY_SPECIALIZED :
1112
+ # simplify specialised opcode variants to their more generic form
1113
+ # (eg: 'LOAD_FAST_BORROW' -> 'LOAD_FAST', etc)
1114
+ updated_params = {"opname" : OpNames .SIMPLIFY_SPECIALIZED [inst .opname ]}
1115
+ if opname == "LOAD_SMALL_INT" :
1116
+ updated_params ["argrepr" ] = str (inst .argval )
1117
+ inst = inst ._replace (** updated_params )
1118
+
1119
+ elif opname == "BINARY_OP" and inst .argrepr == "[]" :
1120
+ # special case for new '[]' binary op; revert to 'BINARY_SUBSCR'
1121
+ inst = inst ._replace (
1122
+ opname = "BINARY_SUBSCR" , arg = None , argval = None , argrepr = ""
1123
+ )
1124
+
1098
1125
return inst
1099
1126
1100
1127
def _is_stdlib_datetime (
0 commit comments