bpo-39156: Break up COMPARE_OP into four logically distinct opcodes. (GH-17754)
Break up COMPARE_OP into four logically distinct opcodes: * COMPARE_OP for rich comparisons * IS_OP for 'is' and 'is not' tests * CONTAINS_OP for 'in' and 'is not' tests * JUMP_IF_NOT_EXC_MATCH for checking exceptions in 'try-except' statements.
This commit is contained in:
parent
62e3973395
commit
9af0e47b17
|
@ -927,6 +927,20 @@ All of the following opcodes use their arguments.
|
|||
``cmp_op[opname]``.
|
||||
|
||||
|
||||
.. opcode:: IS_OP (invert)
|
||||
|
||||
Performs ``is`` comparison, or ``is not`` if ``invert`` is 1.
|
||||
|
||||
.. versionadded:: 3.9
|
||||
|
||||
|
||||
.. opcode:: CONTAINS_OP (invert)
|
||||
|
||||
Performs ``in`` comparison, or ``not in`` if ``invert`` is 1.
|
||||
|
||||
.. versionadded:: 3.9
|
||||
|
||||
|
||||
.. opcode:: IMPORT_NAME (namei)
|
||||
|
||||
Imports the module ``co_names[namei]``. TOS and TOS1 are popped and provide
|
||||
|
@ -961,6 +975,13 @@ All of the following opcodes use their arguments.
|
|||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. opcode:: JUMP_IF_NOT_EXC_MATCH (target)
|
||||
|
||||
Tests whether the second value on the stack is an exception matching TOS,
|
||||
and jumps if it is not. Pops two values from the stack.
|
||||
|
||||
.. versionadded:: 3.9
|
||||
|
||||
|
||||
.. opcode:: JUMP_IF_TRUE_OR_POP (target)
|
||||
|
||||
|
|
|
@ -93,6 +93,9 @@ extern "C" {
|
|||
#define POP_JUMP_IF_FALSE 114
|
||||
#define POP_JUMP_IF_TRUE 115
|
||||
#define LOAD_GLOBAL 116
|
||||
#define IS_OP 117
|
||||
#define CONTAINS_OP 118
|
||||
#define JUMP_IF_NOT_EXC_MATCH 121
|
||||
#define SETUP_FINALLY 122
|
||||
#define LOAD_FAST 124
|
||||
#define STORE_FAST 125
|
||||
|
@ -132,11 +135,6 @@ extern "C" {
|
|||
remaining private.*/
|
||||
#define EXCEPT_HANDLER 257
|
||||
|
||||
|
||||
enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE,
|
||||
PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, PyCmp_IN, PyCmp_NOT_IN,
|
||||
PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD};
|
||||
|
||||
#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -274,6 +274,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# Python 3.9a0 3420 (add LOAD_ASSERTION_ERROR #34880)
|
||||
# Python 3.9a0 3421 (simplified bytecode for with blocks #32949)
|
||||
# Python 3.9a0 3422 (remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY, POP_FINALLY bytecodes #33387)
|
||||
# Python 3.9a2 3423 (add IS_OP, CONTAINS_OP and JUMP_IF_NOT_EXC_MATCH bytecodes #39156)
|
||||
#
|
||||
# MAGIC must change whenever the bytecode emitted by the compiler may no
|
||||
# longer be understood by older implementations of the eval loop (usually
|
||||
|
@ -282,7 +283,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
||||
# in PC/launcher.c must also be updated.
|
||||
|
||||
MAGIC_NUMBER = (3422).to_bytes(2, 'little') + b'\r\n'
|
||||
MAGIC_NUMBER = (3423).to_bytes(2, 'little') + b'\r\n'
|
||||
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
|
||||
|
||||
_PYCACHE = '__pycache__'
|
||||
|
|
|
@ -21,8 +21,7 @@ try:
|
|||
except ImportError:
|
||||
pass
|
||||
|
||||
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
|
||||
'is not', 'exception match', 'BAD')
|
||||
cmp_op = ('<', '<=', '==', '!=', '>', '>=')
|
||||
|
||||
hasconst = []
|
||||
hasname = []
|
||||
|
@ -159,6 +158,10 @@ jabs_op('POP_JUMP_IF_TRUE', 115) # ""
|
|||
|
||||
name_op('LOAD_GLOBAL', 116) # Index in name list
|
||||
|
||||
def_op('IS_OP', 117)
|
||||
def_op('CONTAINS_OP', 118)
|
||||
|
||||
jabs_op('JUMP_IF_NOT_EXC_MATCH', 121)
|
||||
jrel_op('SETUP_FINALLY', 122) # Distance to target address
|
||||
|
||||
def_op('LOAD_FAST', 124) # Local variable number
|
||||
|
|
|
@ -278,34 +278,33 @@ dis_traceback = """\
|
|||
--> 6 BINARY_TRUE_DIVIDE
|
||||
8 POP_TOP
|
||||
10 POP_BLOCK
|
||||
12 JUMP_FORWARD 44 (to 58)
|
||||
12 JUMP_FORWARD 42 (to 56)
|
||||
|
||||
%3d >> 14 DUP_TOP
|
||||
16 LOAD_GLOBAL 0 (Exception)
|
||||
18 COMPARE_OP 10 (exception match)
|
||||
20 POP_JUMP_IF_FALSE 56
|
||||
22 POP_TOP
|
||||
24 STORE_FAST 0 (e)
|
||||
26 POP_TOP
|
||||
28 SETUP_FINALLY 18 (to 48)
|
||||
18 JUMP_IF_NOT_EXC_MATCH 54
|
||||
20 POP_TOP
|
||||
22 STORE_FAST 0 (e)
|
||||
24 POP_TOP
|
||||
26 SETUP_FINALLY 18 (to 46)
|
||||
|
||||
%3d 30 LOAD_FAST 0 (e)
|
||||
32 LOAD_ATTR 1 (__traceback__)
|
||||
34 STORE_FAST 1 (tb)
|
||||
36 POP_BLOCK
|
||||
38 POP_EXCEPT
|
||||
40 LOAD_CONST 0 (None)
|
||||
42 STORE_FAST 0 (e)
|
||||
44 DELETE_FAST 0 (e)
|
||||
46 JUMP_FORWARD 10 (to 58)
|
||||
>> 48 LOAD_CONST 0 (None)
|
||||
50 STORE_FAST 0 (e)
|
||||
52 DELETE_FAST 0 (e)
|
||||
54 RERAISE
|
||||
>> 56 RERAISE
|
||||
%3d 28 LOAD_FAST 0 (e)
|
||||
30 LOAD_ATTR 1 (__traceback__)
|
||||
32 STORE_FAST 1 (tb)
|
||||
34 POP_BLOCK
|
||||
36 POP_EXCEPT
|
||||
38 LOAD_CONST 0 (None)
|
||||
40 STORE_FAST 0 (e)
|
||||
42 DELETE_FAST 0 (e)
|
||||
44 JUMP_FORWARD 10 (to 56)
|
||||
>> 46 LOAD_CONST 0 (None)
|
||||
48 STORE_FAST 0 (e)
|
||||
50 DELETE_FAST 0 (e)
|
||||
52 RERAISE
|
||||
>> 54 RERAISE
|
||||
|
||||
%3d >> 58 LOAD_FAST 1 (tb)
|
||||
60 RETURN_VALUE
|
||||
%3d >> 56 LOAD_FAST 1 (tb)
|
||||
58 RETURN_VALUE
|
||||
""" % (TRACEBACK_CODE.co_firstlineno + 1,
|
||||
TRACEBACK_CODE.co_firstlineno + 2,
|
||||
TRACEBACK_CODE.co_firstlineno + 3,
|
||||
|
@ -506,7 +505,8 @@ class DisTests(unittest.TestCase):
|
|||
def test_widths(self):
|
||||
for opcode, opname in enumerate(dis.opname):
|
||||
if opname in ('BUILD_MAP_UNPACK_WITH_CALL',
|
||||
'BUILD_TUPLE_UNPACK_WITH_CALL'):
|
||||
'BUILD_TUPLE_UNPACK_WITH_CALL',
|
||||
'JUMP_IF_NOT_EXC_MATCH'):
|
||||
continue
|
||||
with self.subTest(opname=opname):
|
||||
width = dis._OPNAME_WIDTH
|
||||
|
@ -1045,63 +1045,62 @@ expected_opinfo_jumpy = [
|
|||
Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=96, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=98, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=100, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='SETUP_FINALLY', opcode=122, arg=98, argval=202, argrepr='to 202', offset=102, starts_line=20, is_jump_target=True),
|
||||
Instruction(opname='SETUP_FINALLY', opcode=122, arg=96, argval=200, argrepr='to 200', offset=102, starts_line=20, is_jump_target=True),
|
||||
Instruction(opname='SETUP_FINALLY', opcode=122, arg=12, argval=118, argrepr='to 118', offset=104, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=106, starts_line=21, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=108, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='BINARY_TRUE_DIVIDE', opcode=27, arg=None, argval=None, argrepr='', offset=110, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=112, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=114, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=28, argval=146, argrepr='to 146', offset=116, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=26, argval=144, argrepr='to 144', offset=116, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=118, starts_line=22, is_jump_target=True),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=2, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=120, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='COMPARE_OP', opcode=107, arg=10, argval='exception match', argrepr='exception match', offset=122, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=144, argval=144, argrepr='', offset=124, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_IF_NOT_EXC_MATCH', opcode=121, arg=142, argval=142, argrepr='', offset=122, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=124, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=126, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=128, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=130, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=132, starts_line=23, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=134, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=136, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=138, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=140, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=46, argval=190, argrepr='to 190', offset=142, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=144, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=146, starts_line=25, is_jump_target=True),
|
||||
Instruction(opname='SETUP_WITH', opcode=143, arg=24, argval=174, argrepr='to 174', offset=148, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=150, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=152, starts_line=26, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=154, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=156, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=158, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=160, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=162, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=130, starts_line=23, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=132, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=134, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=136, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=138, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=46, argval=188, argrepr='to 188', offset=140, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=142, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=144, starts_line=25, is_jump_target=True),
|
||||
Instruction(opname='SETUP_WITH', opcode=143, arg=24, argval=172, argrepr='to 172', offset=146, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=148, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=150, starts_line=26, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=152, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=154, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=156, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=158, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=160, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=162, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=164, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='DUP_TOP', opcode=4, arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=3, argval=3, argrepr='', offset=168, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=170, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=190, argrepr='to 190', offset=172, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=174, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=180, argval=180, argrepr='', offset=176, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=178, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=180, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=3, argval=3, argrepr='', offset=166, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=168, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=16, argval=188, argrepr='to 188', offset=170, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=172, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=178, argval=178, argrepr='', offset=174, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=176, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=178, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=180, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=182, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=184, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=188, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=190, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=192, starts_line=28, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=194, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=196, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=198, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=10, argval=212, argrepr='to 212', offset=200, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=202, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=204, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=206, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=208, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=210, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=212, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=214, starts_line=None, is_jump_target=False)
|
||||
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=184, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=188, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=190, starts_line=28, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=192, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=194, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=196, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='JUMP_FORWARD', opcode=110, arg=10, argval=210, argrepr='to 210', offset=198, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=200, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=202, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=204, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=206, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='RERAISE', opcode=48, arg=None, argval=None, argrepr='', offset=208, starts_line=None, is_jump_target=False),
|
||||
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=210, starts_line=None, is_jump_target=True),
|
||||
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=212, starts_line=None, is_jump_target=False),
|
||||
]
|
||||
|
||||
# One last piece of inspect fodder to check the default line number handling
|
||||
|
|
|
@ -65,14 +65,14 @@ class TestTranforms(BytecodeTestCase):
|
|||
self.check_lnotab(unot)
|
||||
|
||||
def test_elim_inversion_of_is_or_in(self):
|
||||
for line, cmp_op in (
|
||||
('not a is b', 'is not',),
|
||||
('not a in b', 'not in',),
|
||||
('not a is not b', 'is',),
|
||||
('not a not in b', 'in',),
|
||||
for line, cmp_op, invert in (
|
||||
('not a is b', 'IS_OP', 1,),
|
||||
('not a is not b', 'IS_OP', 0,),
|
||||
('not a in b', 'CONTAINS_OP', 1,),
|
||||
('not a not in b', 'CONTAINS_OP', 0,),
|
||||
):
|
||||
code = compile(line, '', 'single')
|
||||
self.assertInBytecode(code, 'COMPARE_OP', cmp_op)
|
||||
self.assertInBytecode(code, cmp_op, invert)
|
||||
self.check_lnotab(code)
|
||||
|
||||
def test_global_as_constant(self):
|
||||
|
|
|
@ -434,11 +434,11 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
|||
def f(x: not (int is int), /): ...
|
||||
|
||||
# without constant folding we end up with
|
||||
# COMPARE_OP(is), UNARY_NOT
|
||||
# with constant folding we should expect a COMPARE_OP(is not)
|
||||
# COMPARE_OP(is), IS_OP (0)
|
||||
# with constant folding we should expect a IS_OP (1)
|
||||
codes = [(i.opname, i.argval) for i in dis.get_instructions(g)]
|
||||
self.assertNotIn(('UNARY_NOT', None), codes)
|
||||
self.assertIn(('COMPARE_OP', 'is not'), codes)
|
||||
self.assertIn(('IS_OP', 1), codes)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
Split the COMPARE_OP bytecode instruction into four distinct instructions.
|
||||
|
||||
* COMPARE_OP for rich comparisons
|
||||
* IS_OP for 'is' and 'is not' tests
|
||||
* CONTAINS_OP for 'in' and 'is not' tests
|
||||
* JUMP_IF_NOT_EXC_MATCH for checking exceptions in 'try-except' statements.
|
||||
|
||||
This improves the clarity of the interpreter and should provide a modest
|
||||
speedup.
|
|
@ -1247,6 +1247,7 @@ static PYC_MAGIC magic_values[] = {
|
|||
{ 3360, 3379, L"3.6" },
|
||||
{ 3390, 3399, L"3.7" },
|
||||
{ 3400, 3419, L"3.8" },
|
||||
{ 3420, 3429, L"3.9" },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
@ -1830,7 +1831,7 @@ process(int argc, wchar_t ** argv)
|
|||
|
||||
#if !defined(VENV_REDIRECT)
|
||||
/* bpo-35811: The __PYVENV_LAUNCHER__ variable is used to
|
||||
* override sys.executable and locate the original prefix path.
|
||||
* override sys.executable and locate the original prefix path.
|
||||
* However, if it is silently inherited by a non-venv Python
|
||||
* process, that process will believe it is running in the venv
|
||||
* still. This is the only place where *we* can clear it (that is,
|
||||
|
|
137
Python/ceval.c
137
Python/ceval.c
|
@ -67,7 +67,6 @@ static void maybe_dtrace_line(PyFrameObject *, int *, int *, int *);
|
|||
static void dtrace_function_entry(PyFrameObject *);
|
||||
static void dtrace_function_return(PyFrameObject *);
|
||||
|
||||
static PyObject * cmp_outcome(PyThreadState *, int, PyObject *, PyObject *);
|
||||
static PyObject * import_name(PyThreadState *, PyFrameObject *,
|
||||
PyObject *, PyObject *, PyObject *);
|
||||
static PyObject * import_from(PyThreadState *, PyObject *, PyObject *);
|
||||
|
@ -2897,12 +2896,13 @@ main_loop:
|
|||
}
|
||||
|
||||
case TARGET(COMPARE_OP): {
|
||||
assert(oparg <= Py_GE);
|
||||
PyObject *right = POP();
|
||||
PyObject *left = TOP();
|
||||
PyObject *res = cmp_outcome(tstate, oparg, left, right);
|
||||
PyObject *res = PyObject_RichCompare(left, right, oparg);
|
||||
SET_TOP(res);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
SET_TOP(res);
|
||||
if (res == NULL)
|
||||
goto error;
|
||||
PREDICT(POP_JUMP_IF_FALSE);
|
||||
|
@ -2910,6 +2910,81 @@ main_loop:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(IS_OP): {
|
||||
PyObject *right = POP();
|
||||
PyObject *left = TOP();
|
||||
int res = (left == right)^oparg;
|
||||
PyObject *b = res ? Py_True : Py_False;
|
||||
Py_INCREF(b);
|
||||
SET_TOP(b);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
PREDICT(POP_JUMP_IF_FALSE);
|
||||
PREDICT(POP_JUMP_IF_TRUE);
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(CONTAINS_OP): {
|
||||
PyObject *right = POP();
|
||||
PyObject *left = POP();
|
||||
int res = PySequence_Contains(right, left);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res < 0) {
|
||||
goto error;
|
||||
}
|
||||
PyObject *b = (res^oparg) ? Py_True : Py_False;
|
||||
Py_INCREF(b);
|
||||
PUSH(b);
|
||||
PREDICT(POP_JUMP_IF_FALSE);
|
||||
PREDICT(POP_JUMP_IF_TRUE);
|
||||
FAST_DISPATCH();
|
||||
}
|
||||
|
||||
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
|
||||
"BaseException is not allowed"
|
||||
|
||||
case TARGET(JUMP_IF_NOT_EXC_MATCH): {
|
||||
PyObject *right = POP();
|
||||
PyObject *left = POP();
|
||||
if (PyTuple_Check(right)) {
|
||||
Py_ssize_t i, length;
|
||||
length = PyTuple_GET_SIZE(right);
|
||||
for (i = 0; i < length; i++) {
|
||||
PyObject *exc = PyTuple_GET_ITEM(right, i);
|
||||
if (!PyExceptionClass_Check(exc)) {
|
||||
_PyErr_SetString(tstate, PyExc_TypeError,
|
||||
CANNOT_CATCH_MSG);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!PyExceptionClass_Check(right)) {
|
||||
_PyErr_SetString(tstate, PyExc_TypeError,
|
||||
CANNOT_CATCH_MSG);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
int res = PyErr_GivenExceptionMatches(left, right);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res > 0) {
|
||||
/* Exception matches -- Do nothing */;
|
||||
}
|
||||
else if (res == 0) {
|
||||
JUMPTO(oparg);
|
||||
}
|
||||
else {
|
||||
goto error;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
case TARGET(IMPORT_NAME): {
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *fromlist = POP();
|
||||
|
@ -4951,62 +5026,6 @@ _PyEval_SliceIndexNotNone(PyObject *v, Py_ssize_t *pi)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
|
||||
"BaseException is not allowed"
|
||||
|
||||
static PyObject *
|
||||
cmp_outcome(PyThreadState *tstate, int op, PyObject *v, PyObject *w)
|
||||
{
|
||||
int res = 0;
|
||||
switch (op) {
|
||||
case PyCmp_IS:
|
||||
res = (v == w);
|
||||
break;
|
||||
case PyCmp_IS_NOT:
|
||||
res = (v != w);
|
||||
break;
|
||||
case PyCmp_IN:
|
||||
res = PySequence_Contains(w, v);
|
||||
if (res < 0)
|
||||
return NULL;
|
||||
break;
|
||||
case PyCmp_NOT_IN:
|
||||
res = PySequence_Contains(w, v);
|
||||
if (res < 0)
|
||||
return NULL;
|
||||
res = !res;
|
||||
break;
|
||||
case PyCmp_EXC_MATCH:
|
||||
if (PyTuple_Check(w)) {
|
||||
Py_ssize_t i, length;
|
||||
length = PyTuple_Size(w);
|
||||
for (i = 0; i < length; i += 1) {
|
||||
PyObject *exc = PyTuple_GET_ITEM(w, i);
|
||||
if (!PyExceptionClass_Check(exc)) {
|
||||
_PyErr_SetString(tstate, PyExc_TypeError,
|
||||
CANNOT_CATCH_MSG);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!PyExceptionClass_Check(w)) {
|
||||
_PyErr_SetString(tstate, PyExc_TypeError,
|
||||
CANNOT_CATCH_MSG);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
res = PyErr_GivenExceptionMatches(v, w);
|
||||
break;
|
||||
default:
|
||||
return PyObject_RichCompare(v, w, op);
|
||||
}
|
||||
v = res ? Py_True : Py_False;
|
||||
Py_INCREF(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
import_name(PyThreadState *tstate, PyFrameObject *f,
|
||||
PyObject *name, PyObject *fromlist, PyObject *level)
|
||||
|
|
|
@ -1021,7 +1021,11 @@ stack_effect(int opcode, int oparg, int jump)
|
|||
case LOAD_ATTR:
|
||||
return 0;
|
||||
case COMPARE_OP:
|
||||
case IS_OP:
|
||||
case CONTAINS_OP:
|
||||
return -1;
|
||||
case JUMP_IF_NOT_EXC_MATCH:
|
||||
return -2;
|
||||
case IMPORT_NAME:
|
||||
return -1;
|
||||
case IMPORT_FROM:
|
||||
|
@ -1502,6 +1506,12 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute)
|
|||
return 0; \
|
||||
}
|
||||
|
||||
|
||||
#define ADDOP_COMPARE(C, CMP) { \
|
||||
if (!compiler_addcompare((C), (cmpop_ty)(CMP))) \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
/* VISIT and VISIT_SEQ takes an ASDL type as their second argument. They use
|
||||
the ASDL name to synthesize the name of the C type and the visit function.
|
||||
*/
|
||||
|
@ -2433,35 +2443,49 @@ check_compare(struct compiler *c, expr_ty e)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
cmpop(cmpop_ty op)
|
||||
static int compiler_addcompare(struct compiler *c, cmpop_ty op)
|
||||
{
|
||||
int cmp;
|
||||
switch (op) {
|
||||
case Eq:
|
||||
return PyCmp_EQ;
|
||||
cmp = Py_EQ;
|
||||
break;
|
||||
case NotEq:
|
||||
return PyCmp_NE;
|
||||
cmp = Py_NE;
|
||||
break;
|
||||
case Lt:
|
||||
return PyCmp_LT;
|
||||
cmp = Py_LT;
|
||||
break;
|
||||
case LtE:
|
||||
return PyCmp_LE;
|
||||
cmp = Py_LE;
|
||||
break;
|
||||
case Gt:
|
||||
return PyCmp_GT;
|
||||
cmp = Py_GT;
|
||||
break;
|
||||
case GtE:
|
||||
return PyCmp_GE;
|
||||
cmp = Py_GE;
|
||||
break;
|
||||
case Is:
|
||||
return PyCmp_IS;
|
||||
ADDOP_I(c, IS_OP, 0);
|
||||
return 1;
|
||||
case IsNot:
|
||||
return PyCmp_IS_NOT;
|
||||
ADDOP_I(c, IS_OP, 1);
|
||||
return 1;
|
||||
case In:
|
||||
return PyCmp_IN;
|
||||
ADDOP_I(c, CONTAINS_OP, 0);
|
||||
return 1;
|
||||
case NotIn:
|
||||
return PyCmp_NOT_IN;
|
||||
ADDOP_I(c, CONTAINS_OP, 1);
|
||||
return 1;
|
||||
default:
|
||||
return PyCmp_BAD;
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
ADDOP_I(c, COMPARE_OP, cmp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond)
|
||||
{
|
||||
|
@ -2526,14 +2550,12 @@ compiler_jump_if(struct compiler *c, expr_ty e, basicblock *next, int cond)
|
|||
(expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
|
||||
ADDOP(c, DUP_TOP);
|
||||
ADDOP(c, ROT_THREE);
|
||||
ADDOP_I(c, COMPARE_OP,
|
||||
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, i))));
|
||||
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, i));
|
||||
ADDOP_JABS(c, POP_JUMP_IF_FALSE, cleanup);
|
||||
NEXT_BLOCK(c);
|
||||
}
|
||||
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
|
||||
ADDOP_I(c, COMPARE_OP,
|
||||
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, n))));
|
||||
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, n));
|
||||
ADDOP_JABS(c, cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next);
|
||||
basicblock *end = compiler_new_block(c);
|
||||
if (end == NULL)
|
||||
|
@ -2976,8 +2998,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
|
|||
|
||||
[tb, val, exc] L1: DUP )
|
||||
[tb, val, exc, exc] <evaluate E1> )
|
||||
[tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1
|
||||
[tb, val, exc, 1-or-0] POP_JUMP_IF_FALSE L2 )
|
||||
[tb, val, exc, exc, E1] JUMP_IF_NOT_EXC_MATCH L2 ) only if E1
|
||||
[tb, val, exc] POP
|
||||
[tb, val] <assign to V1> (or POP if no V1)
|
||||
[tb] POP
|
||||
|
@ -3029,8 +3050,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
|
|||
if (handler->v.ExceptHandler.type) {
|
||||
ADDOP(c, DUP_TOP);
|
||||
VISIT(c, expr, handler->v.ExceptHandler.type);
|
||||
ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
|
||||
ADDOP_JABS(c, POP_JUMP_IF_FALSE, except);
|
||||
ADDOP_JABS(c, JUMP_IF_NOT_EXC_MATCH, except);
|
||||
}
|
||||
ADDOP(c, POP_TOP);
|
||||
if (handler->v.ExceptHandler.name) {
|
||||
|
@ -3873,8 +3893,7 @@ compiler_compare(struct compiler *c, expr_ty e)
|
|||
n = asdl_seq_LEN(e->v.Compare.ops) - 1;
|
||||
if (n == 0) {
|
||||
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, 0));
|
||||
ADDOP_I(c, COMPARE_OP,
|
||||
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, 0))));
|
||||
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, 0));
|
||||
}
|
||||
else {
|
||||
basicblock *cleanup = compiler_new_block(c);
|
||||
|
@ -3885,14 +3904,12 @@ compiler_compare(struct compiler *c, expr_ty e)
|
|||
(expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
|
||||
ADDOP(c, DUP_TOP);
|
||||
ADDOP(c, ROT_THREE);
|
||||
ADDOP_I(c, COMPARE_OP,
|
||||
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, i))));
|
||||
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, i));
|
||||
ADDOP_JABS(c, JUMP_IF_FALSE_OR_POP, cleanup);
|
||||
NEXT_BLOCK(c);
|
||||
}
|
||||
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
|
||||
ADDOP_I(c, COMPARE_OP,
|
||||
cmpop((cmpop_ty)(asdl_seq_GET(e->v.Compare.ops, n))));
|
||||
ADDOP_COMPARE(c, asdl_seq_GET(e->v.Compare.ops, n));
|
||||
basicblock *end = compiler_new_block(c);
|
||||
if (end == NULL)
|
||||
return 0;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -116,11 +116,11 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_POP_JUMP_IF_FALSE,
|
||||
&&TARGET_POP_JUMP_IF_TRUE,
|
||||
&&TARGET_LOAD_GLOBAL,
|
||||
&&TARGET_IS_OP,
|
||||
&&TARGET_CONTAINS_OP,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
|
||||
&&TARGET_SETUP_FINALLY,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_LOAD_FAST,
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
|
||||
#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
|
||||
#define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|
||||
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
|
||||
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH)
|
||||
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE \
|
||||
|| op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
|
||||
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
|
||||
|| op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP || op==JUMP_IF_NOT_EXC_MATCH)
|
||||
#define JUMPS_ON_TRUE(op) (op==POP_JUMP_IF_TRUE || op==JUMP_IF_TRUE_OR_POP)
|
||||
#define GETJUMPTGT(arr, i) (get_arg(arr, i) / sizeof(_Py_CODEUNIT) + \
|
||||
(ABSOLUTE_JUMP(_Py_OPCODE(arr[i])) ? 0 : i+1))
|
||||
|
@ -194,6 +194,7 @@ markblocks(_Py_CODEUNIT *code, Py_ssize_t len)
|
|||
case JUMP_IF_TRUE_OR_POP:
|
||||
case POP_JUMP_IF_FALSE:
|
||||
case POP_JUMP_IF_TRUE:
|
||||
case JUMP_IF_NOT_EXC_MATCH:
|
||||
case JUMP_ABSOLUTE:
|
||||
case SETUP_FINALLY:
|
||||
case SETUP_WITH:
|
||||
|
@ -493,6 +494,7 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
|
|||
case POP_JUMP_IF_TRUE:
|
||||
case JUMP_IF_FALSE_OR_POP:
|
||||
case JUMP_IF_TRUE_OR_POP:
|
||||
case JUMP_IF_NOT_EXC_MATCH:
|
||||
j = blocks[j / sizeof(_Py_CODEUNIT)] * sizeof(_Py_CODEUNIT);
|
||||
break;
|
||||
|
||||
|
|
|
@ -22,11 +22,6 @@ footer = """
|
|||
remaining private.*/
|
||||
#define EXCEPT_HANDLER 257
|
||||
|
||||
|
||||
enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE,
|
||||
PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, PyCmp_IN, PyCmp_NOT_IN,
|
||||
PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD};
|
||||
|
||||
#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
Loading…
Reference in New Issue