1313 from typing_extensions import TypeGuard # type: ignore
1414
1515import bytecode as _bytecode
16- from bytecode .utils import PY311 , PY312 , PY313
16+ from bytecode .utils import PY311 , PY312 , PY313 , PY314
1717
1818# --- Instruction argument tools and
1919
3131
3232BITFLAG2_OPCODES = (_opcode .opmap ["LOAD_SUPER_ATTR" ],) if PY312 else ()
3333
34+ # Binary op opcode which has a dedicated arg
35+ BINARY_OPS = (_opcode .opmap ["BINARY_OP" ],) if PY311 else ()
36+
3437# Intrinsic related opcodes
3538INTRINSIC_1OP = (_opcode .opmap ["CALL_INTRINSIC_1" ],) if PY312 else ()
3639INTRINSIC_2OP = (_opcode .opmap ["CALL_INTRINSIC_2" ],) if PY312 else ()
3740INTRINSIC = INTRINSIC_1OP + INTRINSIC_2OP
3841
42+ # Small integer related opcode
43+ SMALL_INT_OPS = (_opcode .opmap ["LOAD_SMALL_INT" ],) if PY314 else ()
44+
45+ # Special method loading related opcodes
46+ SPECIAL_OPS = (_opcode .opmap ["LOAD_SPECIAL" ],) if PY314 else ()
47+
48+ # Common constant loading related opcodes
49+ COMMON_CONSTANT_OPS = (_opcode .opmap ["LOAD_COMMON_CONSTANT" ],) if PY314 else ()
50+
51+ # Value formatting related opcodes (only handle CONVERT_VALUE and BUILD_INTERPOLATION)
52+ # XXX BUILD_INTERPOLATION in 314 need a bit shift and has 2 args
53+ FORMAT_VALUE_OPS = (
54+ (
55+ _opcode .opmap ["CONVERT_VALUE" ],
56+ _opcode .opmap ["BUILD_INTERPOLATION" ],
57+ )
58+ if PY314
59+ else ((_opcode .opmap ["CONVERT_VALUE" ],) if PY313 else ())
60+ )
61+
3962HASJABS = () if PY313 else _opcode .hasjabs
4063if sys .version_info >= (3 , 13 ):
4164 HASJREL = _opcode .hasjump
5174 _opcode .opmap ["STORE_FAST_LOAD_FAST" ],
5275 _opcode .opmap ["STORE_FAST_STORE_FAST" ],
5376 )
77+ if PY314 :
78+ DUAL_ARG_OPCODES = (
79+ * DUAL_ARG_OPCODES ,
80+ _opcode .opmap ["LOAD_FAST_BORROW_LOAD_FAST_BORROW" ],
81+ )
5482 DUAL_ARG_OPCODES_SINGLE_OPS = {
5583 _opcode .opmap ["LOAD_FAST_LOAD_FAST" ]: ("LOAD_FAST" , "LOAD_FAST" ),
5684 _opcode .opmap ["STORE_FAST_LOAD_FAST" ]: ("STORE_FAST" , "LOAD_FAST" ),
@@ -129,6 +157,8 @@ class BinaryOp(enum.IntEnum):
129157 INPLACE_SUBTRACT = 23
130158 INPLACE_TRUE_DIVIDE = 24
131159 INPLACE_XOR = 25
160+ if PY314 :
161+ SUBSCR = 26
132162
133163
134164@enum .unique
@@ -156,6 +186,35 @@ class Intrinsic2Op(enum.IntEnum):
156186 INTRINSIC_SET_FUNCTION_TYPE_PARAMS = 4
157187
158188
189+ @enum .unique
190+ class FormatValue (enum .IntEnum ):
191+ STR = 1
192+ REPR = 2
193+ ASCII = 3
194+
195+
196+ if PY314 :
197+
198+ @enum .unique
199+ class SpecialMethod (enum .IntEnum ):
200+ """Special method names used with LOAD_SPECIAL"""
201+
202+ __ENTER__ = 0
203+ __EXIT__ = 1
204+ __AENTER__ = 2
205+ __AEXIT__ = 3
206+
207+ @enum .unique
208+ class CommonConstants (enum .IntEnum ):
209+ """Common constants names used with LOAD_COMMON_CONSTANT"""
210+
211+ ASSERTION_ERROR = 0
212+ NOT_IMPLEMENTED_ERROR = 1
213+ BUILTIN_TUPLE = 2
214+ BUILTIN_ALL = 3
215+ BUILTIN_ANY = 4
216+
217+
159218# This make type checking happy but means it won't catch attempt to manipulate an unset
160219# statically. We would need guard on object attribute narrowed down through methods
161220class _UNSET (int ):
@@ -357,6 +416,7 @@ def opcode_has_argument(opcode: int) -> bool:
357416 "FORMAT_SIMPLE" : (- 1 , 1 ), # new in 3.13
358417 "FORMAT_SPEC" : (- 2 , 1 ), # new in 3.13
359418 "TO_BOOL" : (- 1 , 1 ), # new in 3.13
419+ "BUILD_TEMPLATE" : (- 2 , 1 ), # new in 3.14
360420}
361421
362422
@@ -385,6 +445,7 @@ def opcode_has_argument(opcode: int) -> bool:
385445 "FORMAT_VALUE" : lambda effect , arg , jump : (effect - 1 , 1 ),
386446 # FOR_ITER needs TOS to be an iterator, hence a prerequisite of 1 on the stack
387447 "FOR_ITER" : lambda effect , arg , jump : (effect , 0 ) if jump else (- 1 , 2 ),
448+ "BUILD_INTERPOLATION" : lambda effect , arg , jump : (- (2 + (arg & 1 )), 1 ),
388449 ** {
389450 # Instr(UNPACK_* , n) pops 1 and pushes n
390451 k : lambda effect , arg , jump : (- 1 , effect + 1 )
@@ -910,6 +971,28 @@ def _check_arg(self, name: str, opcode: int, arg: InstrArg) -> None:
910971 "Compare, got %s" % (name , type (arg ).__name__ )
911972 )
912973
974+ elif opcode in BINARY_OPS :
975+ if not isinstance (arg , BinaryOp ):
976+ raise TypeError (
977+ "operation %s argument type must be "
978+ "BinaryOp, got %s" % (name , type (arg ).__name__ )
979+ )
980+
981+ # We do not enforce constant immortality since which constants are
982+ # immortal may differ between recompilation and execution.
983+
984+ elif opcode in SMALL_INT_OPS :
985+ if not isinstance (arg , int ):
986+ raise TypeError (
987+ "operation %s argument type must be "
988+ "int, got %s" % (name , type (arg ).__name__ )
989+ )
990+ if arg < 0 or arg > 255 :
991+ raise ValueError (
992+ "operation %s argument type must be an "
993+ "int between 0 and 255, got %s" % (name , arg )
994+ )
995+
913996 elif opcode in INTRINSIC_1OP :
914997 if not isinstance (arg , Intrinsic1Op ):
915998 raise TypeError (
@@ -924,5 +1007,20 @@ def _check_arg(self, name: str, opcode: int, arg: InstrArg) -> None:
9241007 "Intrinsic2Op, got %s" % (name , type (arg ).__name__ )
9251008 )
9261009
1010+ elif opcode in SPECIAL_OPS :
1011+ if not isinstance (arg , SpecialMethod ):
1012+ raise TypeError (
1013+ "operation %s argument type must be "
1014+ "SpecialMethod, got %s" % (name , type (arg ).__name__ )
1015+ )
1016+ elif opcode in COMMON_CONSTANT_OPS :
1017+ if not isinstance (arg , CommonConstants ):
1018+ raise TypeError (
1019+ "operation %s argument type must be "
1020+ "CommonConstants, got %s" % (name , type (arg ).__name__ )
1021+ )
1022+
1023+ # XXX format value handling
1024+
9271025 elif opcode_has_argument (opcode ):
9281026 _check_arg_int (arg , name )
0 commit comments