| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,3 +1,4 @@ | |||
| 1 | + import ast | ||
| 1 | 2 | import dis | |
| 2 | 3 | import gc | |
| 3 | 4 | from itertools import combinations, product | |
@@ -1131,6 +1132,53 @@ def f(self): | |||
| 1131 | 1132 | ||
| 1132 | 1133 | class DirectCfgOptimizerTests(CfgOptimizationTestCase): | |
| 1133 | 1134 | ||
| 1135 | + def test_optimize_cfg_const_index_out_of_range(self): | ||
| 1136 | + insts = [ | ||
| 1137 | + ('LOAD_CONST', 2, 0), | ||
| 1138 | + ('RETURN_VALUE', None, 0), | ||
| 1139 | + ] | ||
| 1140 | + seq = self.seq_from_insts(insts) | ||
| 1141 | + with self.assertRaisesRegex(ValueError, "out of range"): | ||
| 1142 | + _testinternalcapi.optimize_cfg(seq, [0, 1], 0) | ||
| 1143 | + | ||
| 1144 | + def test_optimize_cfg_consts_must_be_list(self): | ||
| 1145 | + insts = [ | ||
| 1146 | + ('LOAD_CONST', 0, 0), | ||
| 1147 | + ('RETURN_VALUE', None, 0), | ||
| 1148 | + ] | ||
| 1149 | + seq = self.seq_from_insts(insts) | ||
| 1150 | + with self.assertRaisesRegex(TypeError, "consts must be a list"): | ||
| 1151 | + _testinternalcapi.optimize_cfg(seq, (0,), 0) | ||
| 1152 | + | ||
| 1153 | + def test_compiler_codegen_metadata_consts_roundtrips_optimize_cfg(self): | ||
| 1154 | + tree = ast.parse("x = (1, 2)", mode="exec", optimize=1) | ||
| 1155 | + insts, meta = _testinternalcapi.compiler_codegen(tree, "<s>", 0) | ||
| 1156 | + consts = meta["consts"] | ||
| 1157 | + self.assertIsInstance(consts, list) | ||
| 1158 | + _testinternalcapi.optimize_cfg(insts, consts, 0) | ||
| 1159 | + | ||
| 1160 | + def test_compiler_codegen_consts_include_none_required_for_implicit_return(self): | ||
| 1161 | + # Module "pass" only needs the const table entry for None once | ||
| 1162 | + # _PyCodegen_AddReturnAtEnd runs. If metadata["consts"] were taken | ||
| 1163 | + # before that, the list would not match LOAD_CONST opargs (here: 0 | ||
| 1164 | + # for None), and optimize_cfg would read out of range. | ||
| 1165 | + tree = ast.parse("pass", mode="exec", optimize=1) | ||
| 1166 | + insts, meta = _testinternalcapi.compiler_codegen(tree, "<s>", 0) | ||
| 1167 | + consts = meta["consts"] | ||
| 1168 | + self.assertEqual(consts, [None]) | ||
| 1169 | + | ||
| 1170 | + load_const = opcode.opmap["LOAD_CONST"] | ||
| 1171 | + self.assertEqual( | ||
| 1172 | + [t[1] for t in insts.get_instructions() if t[0] == load_const], | ||
| 1173 | + [0], | ||
| 1174 | + ) | ||
| 1175 | + | ||
| 1176 | + # As if consts were snapshotted before AddReturnAtEnd: still LOAD_CONST 0, no row. | ||
| 1177 | + with self.assertRaisesRegex(ValueError, "out of range"): | ||
| 1178 | + _testinternalcapi.optimize_cfg(insts, [], 0) | ||
| 1179 | + | ||
| 1180 | + _testinternalcapi.optimize_cfg(insts, list(consts), 0) | ||
| 1181 | + | ||
| 1134 | 1182 | def cfg_optimization_test(self, insts, expected_insts, | |
| 1135 | 1183 | consts=None, expected_consts=None, | |
| 1136 | 1184 | nlocals=0): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1081,13 +1081,17 @@ _testinternalcapi.compiler_codegen -> object | |||
| 1081 | 1081 | compile_mode: int = 0 | |
| 1082 | 1082 | ||
| 1083 | 1083 | Apply compiler code generation to an AST. | |
| 1084 | + | ||
| 1085 | + Return (instruction_sequence, metadata). metadata maps "argcount", | ||
| 1086 | + "posonlyargcount", "kwonlyargcount" to ints and "consts" to the list of | ||
| 1087 | + constants in LOAD_CONST index order (for use with optimize_cfg). | ||
| 1084 | 1088 | [clinic start generated code]*/ | |
| 1085 | 1089 | ||
| 1086 | 1090 | static PyObject * | |
| 1087 | 1091 | _testinternalcapi_compiler_codegen_impl(PyObject *module, PyObject *ast, | |
| 1088 | 1092 | PyObject *filename, int optimize, | |
| 1089 | 1093 | int compile_mode) | |
| 1090 | - /*[clinic end generated code: output=40a68f6e13951cc8 input=a0e00784f1517cd7]*/ | ||
| 1094 | + /*[clinic end generated code: output=40a68f6e13951cc8 input=e0c65e5c80efe30e]*/ | ||
| 1091 | 1095 | { | |
| 1092 | 1096 | PyCompilerFlags *flags = NULL; | |
| 1093 | 1097 | return _PyCompile_CodeGen(ast, filename, flags, optimize, compile_mode); | |
@@ -1103,12 +1107,15 @@ _testinternalcapi.optimize_cfg -> object | |||
| 1103 | 1107 | nlocals: int | |
| 1104 | 1108 | ||
| 1105 | 1109 | Apply compiler optimizations to an instruction list. | |
| 1110 | + | ||
| 1111 | + consts must be a list aligned with LOAD_CONST opargs (the "consts" entry | ||
| 1112 | + from the metadata dict returned by compiler_codegen for the same unit). | ||
| 1106 | 1113 | [clinic start generated code]*/ | |
| 1107 | 1114 | ||
| 1108 | 1115 | static PyObject * | |
| 1109 | 1116 | _testinternalcapi_optimize_cfg_impl(PyObject *module, PyObject *instructions, | |
| 1110 | 1117 | PyObject *consts, int nlocals) | |
| 1111 | - /*[clinic end generated code: output=57c53c3a3dfd1df0 input=6a96d1926d58d7e5]*/ | ||
| 1118 | + /*[clinic end generated code: output=57c53c3a3dfd1df0 input=905c3d935e063b27]*/ | ||
| 1112 | 1119 | { | |
| 1113 | 1120 | return _PyCompile_OptimizeCfg(instructions, consts, nlocals); | |
| 1114 | 1121 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1658,6 +1658,7 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags, | |||
| 1658 | 1658 | { | |
| 1659 | 1659 | PyObject *res = NULL; | |
| 1660 | 1660 | PyObject *metadata = NULL; | |
| 1661 | + PyObject *consts_list = NULL; | ||
| 1661 | 1662 | ||
| 1662 | 1663 | if (!PyAST_Check(ast)) { | |
| 1663 | 1664 | PyErr_SetString(PyExc_TypeError, "expected an AST"); | |
@@ -1712,12 +1713,23 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename, PyCompilerFlags *pflags, | |||
| 1712 | 1713 | } | |
| 1713 | 1714 | ||
| 1714 | 1715 | if (_PyInstructionSequence_ApplyLabelMap(_PyCompile_InstrSequence(c)) < 0) { | |
| 1715 | - return NULL; | ||
| 1716 | + goto finally; | ||
| 1717 | + } | ||
| 1718 | + | ||
| 1719 | + /* After AddReturnAtEnd: co_consts indices match the final instruction stream. */ | ||
| 1720 | + consts_list = consts_dict_keys_inorder(umd->u_consts); | ||
| 1721 | + if (consts_list == NULL) { | ||
| 1722 | + goto finally; | ||
| 1723 | + } | ||
| 1724 | + if (PyDict_SetItemString(metadata, "consts", consts_list) < 0) { | ||
| 1725 | + goto finally; | ||
| 1716 | 1726 | } | |
| 1727 | + | ||
| 1717 | 1728 | /* Allocate a copy of the instruction sequence on the heap */ | |
| 1718 | 1729 | res = _PyTuple_FromPair((PyObject *)_PyCompile_InstrSequence(c), metadata); | |
| 1719 | 1730 | ||
| 1720 | 1731 | finally: | |
| 1732 | + Py_XDECREF(consts_list); | ||
| 1721 | 1733 | Py_XDECREF(metadata); | |
| 1722 | 1734 | _PyCompile_ExitScope(c); | |
| 1723 | 1735 | compiler_free(c); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1309,6 +1309,14 @@ get_const_value(int opcode, int oparg, PyObject *co_consts) | |||
| 1309 | 1309 | PyObject *constant = NULL; | |
| 1310 | 1310 | assert(loads_const(opcode)); | |
| 1311 | 1311 | if (opcode == LOAD_CONST) { | |
| 1312 | + assert(PyList_Check(co_consts)); | ||
| 1313 | + Py_ssize_t n = PyList_GET_SIZE(co_consts); | ||
| 1314 | + if (oparg < 0 || oparg >= n) { | ||
| 1315 | + PyErr_Format(PyExc_ValueError, | ||
| 1316 | + "LOAD_CONST index %d is out of range for consts (len=%zd)", | ||
| 1317 | + oparg, n); | ||
| 1318 | + return NULL; | ||
| 1319 | + } | ||
| 1312 | 1320 | constant = PyList_GET_ITEM(co_consts, oparg); | |
| 1313 | 1321 | } | |
| 1314 | 1322 | if (opcode == LOAD_SMALL_INT) { | |
@@ -2167,6 +2175,9 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject * | |||
| 2167 | 2175 | cfg_instr *inst = &bb->b_instr[i]; | |
| 2168 | 2176 | if (inst->i_opcode == LOAD_CONST) { | |
| 2169 | 2177 | PyObject *constant = get_const_value(inst->i_opcode, inst->i_oparg, consts); | |
| 2178 | + if (constant == NULL) { | ||
| 2179 | + return ERROR; | ||
| 2180 | + } | ||
| 2170 | 2181 | int res = maybe_instr_make_load_smallint(inst, constant, consts, const_cache); | |
| 2171 | 2182 | Py_DECREF(constant); | |
| 2172 | 2183 | if (res < 0) { | |
@@ -4064,6 +4075,10 @@ _PyCompile_OptimizeCfg(PyObject *seq, PyObject *consts, int nlocals) | |||
| 4064 | 4075 | PyErr_SetString(PyExc_ValueError, "expected an instruction sequence"); | |
| 4065 | 4076 | return NULL; | |
| 4066 | 4077 | } | |
| 4078 | + if (!PyList_Check(consts)) { | ||
| 4079 | + PyErr_SetString(PyExc_TypeError, "consts must be a list"); | ||
| 4080 | + return NULL; | ||
| 4081 | + } | ||
| 4067 | 4082 | PyObject *const_cache = PyDict_New(); | |
| 4068 | 4083 | if (const_cache == NULL) { | |
| 4069 | 4084 | return NULL; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments